package ProGAL.geom2d; import java.awt.Color; import java.util.List; import ProGAL.geom2d.viewer.J2DScene; //import ProGAL.geom3d.Point; //import ProGAL.geom3d.kineticDelaunay.Vertex; import ProGAL.math.Constants; import ProGAL.math.Functions; import ProGAL.math.Trigonometry; public class Circle implements Shape{ protected Point center; protected double radius; /** Construct a circle with the given center and radius. */ public Circle(Point center, double radius) { this.center = center; this.radius = radius; } /** Constructs a circle that is a copy of a given circle */ public Circle(Circle c) { center = new Point(c.center.x(), c.center.y()); radius = c.radius; } /** Constructs a circle through two given points and wth their midpoint as the center*/ public Circle(Point p1, Point p2){ this( Point.midPoint(p1, p2), p1.distance(p2)/2 ); } /** Constructs a circle through 3 given points. */ public Circle(Point a, Point b, Point c) { if (a.equals(b) || b.equals(c)) { center = Point.midPoint(a, c); radius = center.distance(a); } else if (a.equals(c)) { center = Point.midPoint(a, b); radius = center.distance(a); } else { Line ab = Point.getBisector(a, b); Line bc = Point.getBisector(b, c); if (ab.isParallelWith(bc)) { Point p1 = a; Point p2 = b; if (a.distance(b) < a.distance(c)) p2 = c; if (b.distance(c) > p1.distance(p2)) p1 = b; center = Point.midPoint(p1, p2); radius = p1.distance(p2); } else { center = Line.getIntersection(ab, bc); if (center == null) throw new Error(a+" "+b+" "+c); radius = center.distance(b); } } } // /** // * Creates circle through 2 given points and with center on given line // */ // public static Circle createEnclosingCircleOnLine(Line2d ab, Point b, Point c) { // Line2d bc = Point.getBisector(b, c); // center = Line2d.getIntersection(ab, bc); // radius = center.getDistance(c); // } // /** Creates smallest circle containing two given circles */ public Circle(Circle c1, Circle c2){ if(c1.contains(c2)){ center = c1.center.clone(); radius = c1.radius; }else if(c2.contains(c1)){ center = c2.center.clone(); radius = c2.radius; }else if(c1.center.equals(c2.center)){ center = c1.center.clone(); radius = Math.max(c1.radius, c2.radius); }else{ Line l = new Line(c1.center, c2.center); center = l.getPoint(0.5*c1.center.distance(c2.center)-c1.radius/2+c2.radius/2); radius = center.distance(c1.center)+c1.radius; } } public Circle(Circle circle1, Circle circle2, Circle circle3) { Circle c1 = circle1; Circle c2 = circle2; Circle c3 = circle3; Circle tmp; if( (tmp=new Circle(c1,c2)).contains(c3)) { center = tmp.center; radius = tmp.radius; return;} if( (tmp=new Circle(c1,c3)).contains(c2)) { center = tmp.center; radius = tmp.radius; return;} if( (tmp=new Circle(c2,c3)).contains(c1)) { center = tmp.center; radius = tmp.radius; return;} Circle mec = ApolloniusSolver.solveApollonius(c1, c2, c3, 1, 1, 1);//68HOps this.center = mec.center; this.radius = mec.radius; } public Circle(Triangle tri){ this(tri.getCorner(0), tri.getCorner(1), tri.getCorner(2)); } public Point center() { return center; } public double getRadius() { return radius; } public void setCenter(Point p) { center = p; } public void setRadius(double r) { radius = r; } public void translate(Vector v) { center.addThis(v); } public Double enteringAngle(Point p, Circle C, boolean ccw) { double centerDist = center.distance(C.center); if (centerDist < C.radius + radius) { double alpha = Math.acos(((p.x()-center.x())*(C.center.x()-center.x()) + (p.y()-center.y())*(C.center.y()-center.y()))/(radius*centerDist)); double beta = Math.acos((centerDist*centerDist + radius*radius - C.radius*C.radius)/(2*centerDist*radius)); if (ccw) { if (Point.rightTurn(C.center, center, p)) alpha = alpha + Math.PI - beta; else alpha = alpha - beta; } else { if (Point.leftTurn(C.center, center, p)) alpha = alpha + beta- 2*Math.PI; else alpha = beta - alpha; } System.out.println("alpha = " + Functions.toDeg(alpha)); return alpha; } return null; } public Double exitingAngle(Point p, Circle C, boolean ccw) { double centerDist = center.distance(C.center); Double alpha = Math.acos(((p.x()-center.x())*(C.center.x()-center.x()) + (p.y()-center.y())*(C.center.y()-center.y()))/(radius*centerDist)); Double beta = Math.acos((centerDist*centerDist + radius*radius - C.radius*C.radius)/(2*centerDist*radius)); if (ccw) { if (Point.rightTurn(C.center, center, p)) alpha = alpha - beta; else alpha = alpha + beta; } else { if (Point.leftTurn(C.center, center, p)) alpha = beta - alpha; else alpha = -alpha - beta; } System.out.println("alpha = " + Functions.toDeg(alpha)); return alpha; } public Double enteringAngle(Point p, Line L, boolean ccw) { double dist = L.getDistance(p); if (dist < radius) { Point q = L.projectPoint(center); double alpha = Math.acos(((p.x()-center.x())*(q.x()-center.x()) + (p.y()-center.y())*(q.y()-center.y()))/(radius*dist)); double beta = Math.acos(dist/radius); if (ccw) { if (Point.rightTurn(q, center, p)) alpha = 2*Math.PI - alpha; } else { alpha = -alpha; beta = -beta; if (Point.leftTurn(q, center, p)) alpha = -2*Math.PI - alpha; } System.out.println("alpha = " + Functions.toDeg(alpha)); System.out.println("beta = " + Functions.toDeg(beta)); return alpha - beta; } return null; } /** returns 0-2 intersections of this circle with another circle */ public Point[] intersections(Circle c){ Point[] ret = new Point[2]; double centerDist = center.distance(c.center); if (centerDist < c.radius+radius) { // nested circles, no intersection if ((centerDist + radius < c.radius) || (centerDist + c.radius < radius)) return null; // nested circles, touch point from inside: this cricle is inside c if (centerDist + radius == c.radius) { ret[0] = c.center.add(c.center.vectorTo(center).scaleToLength(c.radius)); ret[1] = null; return ret; } // nested circles, touch point from inside: c is inside this circle if (centerDist + c.radius == radius) { ret[0] = center.add(center.vectorTo(c.center).scaleToLength(radius)); ret[1] = null; return ret; } // two intersections double dSq = c.center.distanceSquared(center); double d = Math.sqrt(dSq); double RSq = radius*radius; double rSq = c.radius*c.radius; double tmp = dSq-rSq+RSq; double d1 = tmp/(2*d); double a = Math.sqrt( 4*dSq*RSq - tmp*tmp )/d; Vector x = center.vectorTo(c.center).normalizeThis(); Vector y = x.rotate90(); x.multiplyThis(d1); y.multiplyThis(a/2); ret[0] = center.add(x).addThis(y); ret[1] = center.add(x).subtractThis(y); return ret; } // touch point from outside if (centerDist == c.radius+radius){ ret[0] = center.add(center.vectorTo(c.center).scaleToLength(radius)); ret[1] = null; return ret; } // circles are disjoint return null; } /** returns 0-2 intersections of this circle with a line, * source: http://mathworld.wolfram.com/Circle-LineIntersection.html * some odd errors corrected 01-09-2013 - PW */ public Point[] intersections(Line l) { double dx = l.n.y(); double dy = -l.n.x(); double x1 = l.p.x() - center.x(); double y1 = l.p.y() - center.y(); double x2 = x1 - dx; double y2 = y1 - dy; double dr2 = dx*dx + dy*dy; double D = x1*y2 - x2*y1; double delta = radius*radius*dr2 - D*D; if (delta < -Constants.EPSILON) return null; if (delta < Constants.EPSILON) { Point[] ret = new Point[1]; ret[0] = new Point(center.x() + D*dy/dr2, center.y() - D*dx/dr2); return ret; } Point[] ret = new Point[2]; double deltaRoot = Math.sqrt(delta); if (dy < 0.0) { ret[0] = new Point(center.x() + (D*dy - dx*deltaRoot)/dr2, center.y() - (D*dx + dy*deltaRoot)/dr2); ret[1] = new Point(center.x() + (D*dy + dx*deltaRoot)/dr2, center.y() - (D*dx - dy*deltaRoot)/dr2); } else { ret[0] = new Point(center.x() + (D*dy + dx*deltaRoot)/dr2, center.y() - (D*dx - dy*deltaRoot)/dr2); ret[1] = new Point(center.x() + (D*dy - dx*deltaRoot)/dr2, center.y() - (D*dx + dy*deltaRoot)/dr2); } return ret; } public static Circle minimumEnclosingCircle_Welzl(List points){ Point[] b = new Point[3]; return findMEC(points.size(), points,0,b); } private static Circle findMEC(int n, List points, int m, Point[] b){ Circle mec = new Circle(new Point(0,0),0); // Compute the Smallest Enclosing Circle defined by B if(m == 1) mec = new Circle(b[0],0); else if(m == 2) mec = new Circle(b[0], b[1]); else if(m == 3) return new Circle( b[0], b[1], b[2]); // Check if all the points in p are enclosed for(int i=0; i points, double eps) { for (Point p : points) if (contains(p, eps)) return false; return true; } boolean isOrthogonal(Circle c) { return getPowerDistance(c) == 0.0; } @Deprecated public static Circle minimumEnclosingCircle_bruteforce(List points){ System.err.println("Warning: Please only use Circle.minimumEnclosingCircle_bruteforce for testing purposes!!"); //System.out.println("MEC .. "); //for(Point p: points) System.out.println(p); //System.out.println("Checking pairs"); double minRad = Double.POSITIVE_INFINITY; Circle minCircle = null; for(int i=0;i=center.distance(c.center)+c.radius-0.000001; } public boolean onCircle(Point p) { return Math.abs(center.distanceSquared(p) - radius* radius) < Constants.EPSILON; } public String toString() { return toString(2); } public String toString(int dec) { return String.format("Circle[%s,%"+dec+"f]",center,radius); } public void toConsole() { toConsole(2); } public void toConsole(int dec) { System.out.println(toString(dec)); } public void toScene(J2DScene scene) { scene.addShape(this, Color.black); } public void toScene(J2DScene scene, Color clr) { scene.addShape(this, clr); } public Point getCenter() { return center; } /* Point origo = new Point(0,0); Circle circum = new Circle(s, r1, r2); LineSegment sr1 = new LineSegment(s, r1); LineSegment r1r2 = new LineSegment(r1, r2); LineSegment r2s = new LineSegment(r2, s); Circle r1c = new Circle(origo, origo.distance(r1)); Circle r2c = new Circle(origo, origo.distance(r2)); J2DScene scene = J2DScene.createJ2DSceneInFrame(); r1c.toScene(scene); r2c.toScene(scene); circum.toScene(scene); sr1.toScene(scene); r1r2.toScene(scene); r2s.toScene(scene); s.toScene(scene, 0.05, Color.black); r1.toScene(scene, 0.05, Color.blue); r2.toScene(scene, 0.05, Color.blue); p.toScene(scene, 0.05, Color.red); Circle cir; circum.center.toScene(scene, 0.03, Color.pink); double cos = Math.cos(0.005); double sin = Math.sin(0.005); for (double angle = 0; angle < 2*Math.PI; angle = angle + 0.002) { r1.rotation(cos, sin); r2.rotation(cos, sin); cir = new Circle(s, r1, r2); circum.center = cir.center; circum.center.toScene(scene, 0.03, Color.pink); circum.radius = cir.radius; try { Thread.sleep(300); } catch (InterruptedException e) {} scene.repaint(); } } */ }