Try to Draw a Perfect Circle
Sometimes information technology is really useful to spend some time reinventing the bicycle. As you might take already noticed there are a lot of frameworks, merely it is non that hard to implement a uncomplicated, but yet useful solution without introducing all that complexity. (Delight don't get me incorrect, for whatsoever serious purpose it is better to use some mature and proven to be stable framework).
I volition nowadays my results get-go so explain the unproblematic and straightforward idea behind them.
Yous'll see in my implementation there is no need to analyze every unmarried betoken and do complex computations. The thought is to spot some valuable meta information. I volition apply tangent every bit an instance:
Let's place a simple and straightforward blueprint, typical for the selected shape:
So information technology is not that hard to implement a circumvolve detection machinery based on that idea. See working demo below (Sad, I'm using Java every bit the fastest way to provide this fast and a fleck dirty case):
import coffee.awt.BasicStroke; import coffee.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import coffee.awt.HeadlessException; import java.awt.Point; import coffee.awt.RenderingHints; import java.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 } individual static final Type[] circleShape = { Type.RIGHT_DOWN, Type.LEFT_DOWN, Blazon.LEFT_UP, Type.RIGHT_UP}; individual boolean editing = false; private Point[] bounds; individual Signal last = new Point(0, 0); private List<Point> points = new ArrayList<>(); public CircleGestureDemo() throws HeadlessException { super("Detect 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 k = (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); g.setRenderingHints(qualityHints); yard.setColor(Colour.RED); if (cD == 0) { Betoken b = null; for (Signal eastward : points) { if (nada != b) { chiliad.drawLine(b.x, b.y, e.x, eastward.y); } b = due east; } }else if (cD > 0){ one thousand.setColor(Color.BLUE); g.setStroke(new BasicStroke(3)); g.drawOval(cX, cY, cD, cD); }else{ g.drawString("Uknown",30,50); } } private Type getType(int dx, int dy) { Type upshot = Blazon.UNDEFINED; if (dx > 0 && dy < 0) { effect = Type.RIGHT_DOWN; } else if (dx < 0 && dy < 0) { issue = Type.LEFT_DOWN; } else if (dx < 0 && dy > 0) { result = Type.LEFT_UP; } else if (dx > 0 && dy > 0) { result = Type.RIGHT_UP; } return result; } private boolean isCircle(List<Betoken> points) { boolean consequence = false; Blazon[] shape = circleShape; Type[] detected = new Type[shape.length]; bounds = new Signal[shape.length]; terminal int Step = 5; int index = 0; Point electric current = points.become(0); Blazon type = null; for (int i = STEP; i < points.size(); i += STEP) { Indicate next = points.get(i); int dx = adjacent.10 - current.ten; int dy = -(next.y - current.y); if(dx == 0 || dy == 0) { continue; } Type newType = getType(dx, dy); if(type == zip || type != newType) { if(newType != shape[index]) { interruption; } premises[alphabetize] = electric current; detected[index++] = newType; } type = newType; electric current = side by side; if (index >= shape.length) { issue = true; interruption; } } return result; } @Override public void mousePressed(MouseEvent e) { cD = 0; points.clear(); editing = true; } private int cX; private int cY; private int cD; @Override public void mouseReleased(MouseEvent eastward) { editing = false; if(points.size() > 0) { if(isCircle(points)) { cX = bounds[0].x + Math.abs((premises[2].x - bounds[0].x)/ii); cY = bounds[0].y; cD = bounds[2].y - premises[0].y; cX = cX - cD/ii; Arrangement.out.println("circle"); }else{ cD = -ane; System.out.println("unknown"); } repaint(); } } @Override public void mouseDragged(MouseEvent e) { Indicate newPoint = e.getPoint(); if (editing && !last.equals(newPoint)) { points.add together(newPoint); concluding = newPoint; repaint(); } } @Override public void mouseMoved(MouseEvent eastward) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { } public static void principal(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { CircleGestureDemo t = new CircleGestureDemo(); t.setVisible(true); } }); } }
It should not be a trouble to implement similar beliefs on iOS, since you merely demand several events and coordinates. Something similar the following (meet instance):
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)outcome { UITouch* touch on = [[event allTouches] anyObject]; } - (void)handleTouch:(UIEvent *)event { UITouch* bear on = [[outcome allTouches] anyObject]; CGPoint location = [bear on locationInView:self]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [cocky handleTouch: outcome]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)effect { [cocky handleTouch: event]; }
There are several enhancements possible.
Beginning at any point
Electric current requirement is to commencement drawing a circle from the tiptop heart point due to the post-obit simplification:
if(type == null || type != newType) { if(newType != shape[index]) { break; } bounds[index] = electric current; detected[index++] = newType; }
Please observe the default value of alphabetize
is used. A simple search through the available "parts" of the shape will remove that limitation. Please note you'll need to use a circular buffer in order to detect a full shape:
Clockwise and counterclockwise
In society to support both modes you volition need to use the circular buffer from the previous enhancement and search in both directions:
Draw an ellipse
You accept everything you demand already in the bounds
assortment.
Only employ that data:
cWidth = bounds[ii].y - bounds[0].y; cHeight = premises[3].y - bounds[i].y;
Other gestures (optional)
Finally, you just demand to properly handle a situation when dx
(or dy
) is equal to zip in club to back up other gestures:
Update
This small-scale PoC got quite a loftier attention, and then I did update the code a bit in order to make information technology piece of work smoothly and provide some cartoon hints, highlight supporting points, etc:
Here is the code:
import coffee.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Colour; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import coffee.awt.HeadlessException; import java.awt.Point; import java.awt.RenderingHints; import coffee.awt.consequence.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import coffee.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class CircleGestureDemo extends JFrame { enum Type { RIGHT_DOWN, LEFT_DOWN, LEFT_UP, RIGHT_UP, UNDEFINED } individual static final Type[] circleShape = { Blazon.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 = faux; private Point[] premises; individual Point last = new Point(0, 0); private final Listing<Bespeak> points = new ArrayList<>(); public GesturePanel() { super(truthful); 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); g.setRenderingHints(qualityHints); if (!points.isEmpty() && cD == 0) { isCircle(points, g); grand.setColor(HINT_COLOR); if (bounds[2] != zippo) { int r = (bounds[2].y - bounds[0].y) / 2; m.setStroke(new BasicStroke(r / 3 + one)); g.drawOval(bounds[0].ten - r, premises[0].y, 2 * r, two * r); } else if (premises[one] != nix) { int r = bounds[1].10 - bounds[0].x; yard.setStroke(new BasicStroke(r / iii + 1)); g.drawOval(bounds[0].x - r, bounds[0].y, 2 * r, 2 * r); } } g.setStroke(new BasicStroke(2)); yard.setColor(Colour.RED); if (cD == 0) { Betoken b = null; for (Point e : points) { if (goose egg != b) { g.drawLine(b.x, b.y, e.x, e.y); } b = e; } } else if (cD > 0) { g.setColor(Color.Blueish); 1000.setStroke(new BasicStroke(3)); grand.drawOval(cX, cY, cD, cD); } else { g.drawString("Uknown", 30, l); } } private Blazon getType(int dx, int dy) { Type result = Type.UNDEFINED; if (dx > 0 && dy < 0) { effect = Blazon.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) { consequence = Blazon.RIGHT_UP; } return result; } private boolean isCircle(List<Point> points, Graphics2D g) { boolean consequence = false; Type[] shape = circleShape; bounds = new Point[shape.length]; final int Pace = five; int index = 0; int initial = 0; Point current = points.get(0); Type type = null; for (int i = STEP; i < points.size(); i += Pace) { terminal Point next = points.get(i); final int dx = next.ten - current.x; concluding int dy = -(next.y - current.y); if (dx == 0 || dy == 0) { continue; } final int mark = 8; if (naught != yard) { g.setColor(Colour.Black); chiliad.setStroke(new BasicStroke(2)); g.drawOval(electric current.x - marker/two, current.y - marker/2, marking, mark); } Blazon newType = getType(dx, dy); if (type == cypher || type != newType) { if (newType != shape[alphabetize]) { break; } bounds[alphabetize++] = current; } type = newType; current = next; initial = i; if (index >= shape.length) { result = truthful; break; } } render result; } @Override public void mousePressed(MouseEvent east) { cD = 0; points.clear(); editing = truthful; } private int cX; private int cY; private int cD; @Override public void mouseReleased(MouseEvent eastward) { editing = false; if (points.size() > 0) { if (isCircle(points, zero)) { int r = Math.abs((bounds[two].y - bounds[0].y) / 2); cX = bounds[0].10 - r; cY = bounds[0].y; cD = 2 * r; } else { cD = -one; } repaint(); } } @Override public void mouseDragged(MouseEvent due east) { Point newPoint = e.getPoint(); if (editing && !final.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 e) { } @Override public void mouseClicked(MouseEvent e) { } } public static void chief(Cord[] 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); }
mickelsonficiones.blogspot.com
Source: https://stackoverflow.com/questions/18934805/draw-a-perfect-circle-from-users-touch
Post a Comment for "Try to Draw a Perfect Circle"