public Point2DDouble[] intersect(Circle2DDouble c, DoubleComparator dblComp) { double d = p.distance(c.p); if (dblComp.compare(d, radius + c.radius) > 0 || dblComp.compare(d + Math.min(radius, c.radius), Math.max(radius, c.radius)) < 0) { return new Point2DDouble[0]; } if (dblComp.compare(d, 0) == 0) { return null; } double diff = (radius * radius - c.radius * c.radius) / d; double d1 = (d + diff) * .5; Point2DDouble v = c.p.subtract(p); Point2DDouble v1 = v.multiply(d1 / v.length()); double z = radius * radius - d1 * d1; Point2DDouble p0 = p.add(v1); // if (dblComp.compare(z, 0) < 0) { // System.out.println(this + " " + c); // System.out.println(d + " " + (radius + c.radius)); // throw new AssertionError(); // } if (dblComp.compare(z, 0) <= 0) { return new Point2DDouble[] {p0}; } z = Math.sqrt(z); Point2DDouble v2 = new Point2DDouble(-v.y, v.x).multiply(z / v.length()); return new Point2DDouble[] {p0.add(v2), p0.subtract(v2)}; }
public Point2DDouble[][] getInternalTangents(Circle2DDouble c, DoubleComparator comp) { double d = p.distance(c.p); if (comp.compare(d, radius + c.radius) < 0) return new Point2DDouble[0][]; if (comp.compare(d, radius + c.radius) == 0) { Point2DDouble v = c.p.subtract(p); v = v.multiply(radius / v.length()); v = v.add(p); return new Point2DDouble[][] {{v, v}}; } double nRadius = radius + c.radius; Point2DDouble[] tangentsFromPoint = new Circle2DDouble(p, nRadius).getTangents(c.p, comp); Point2DDouble vn1 = tangentsFromPoint[0].subtract(c.p); vn1 = new Point2DDouble(-vn1.y, vn1.x); vn1 = vn1.multiply(c.radius / vn1.length()); Point2DDouble vn2 = tangentsFromPoint[1].subtract(c.p); vn2 = new Point2DDouble(vn2.y, -vn2.x); vn2 = vn2.multiply(c.radius / vn2.length()); Point2DDouble[][] ret = new Point2DDouble[][] { {tangentsFromPoint[0].add(vn1), c.p.add(vn1)}, {tangentsFromPoint[1].add(vn2), c.p.add(vn2)} }; if (comp.compare(ret[0][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[0][1].distance(c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][1].distance(c.p), c.radius) != 0) throw new AssertionError(); return ret; }
public Point2DDouble[] getTangents(Point2DDouble q, DoubleComparator comp) { double d = p.distance(q); if (comp.compare(d, radius) < 0) { return null; } if (comp.compare(d, radius) == 0) { return new Point2DDouble[] {p}; } double d1 = radius * radius / d; Point2DDouble v = q.subtract(p); Point2DDouble vn = new Point2DDouble(-v.y, v.x); v = v.multiply(d1 / v.length()); Point2DDouble c = p.add(v); double d2 = radius * radius - d1 * d1; if (d2 < 0) d2 = 0; d2 = Math.sqrt(d2); vn = vn.multiply(d2 / vn.length()); return new Point2DDouble[] {c.add(vn), c.subtract(vn)}; }
public Point2DDouble[][] getExternalTangents(Circle2DDouble c, DoubleComparator comp) { if (comp.compare(radius, c.radius) < 0) { Point2DDouble[][] ret = c.getExternalTangents(this, comp); for (Point2DDouble[] e : ret) { Point2DDouble t = e[0]; e[0] = e[1]; e[1] = t; } if (comp.compare(ret[0][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[0][1].distance(c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][1].distance(c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[0][0], ret[0][1], p), radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[0][0], ret[0][1], c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[1][0], ret[1][1], p), radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[1][0], ret[1][1], c.p), c.radius) != 0) throw new AssertionError(); return ret; } double d = p.distance(c.p); if (comp.compare(d + Math.min(radius, c.radius), Math.max(radius, c.radius)) < 0) { return new Point2DDouble[0][]; } if (comp.compare(d + Math.min(radius, c.radius), Math.max(radius, c.radius)) == 0) { Point2DDouble v = c.p.subtract(p); v = v.multiply(radius / v.length()); v = v.add(p); return new Point2DDouble[][] {{v, v}}; } Point2DDouble[] tangentsFromPoint = new Circle2DDouble(p, radius - c.radius).getTangents(c.p, comp); Point2DDouble vn1 = tangentsFromPoint[0].subtract(c.p); vn1 = new Point2DDouble(vn1.y, -vn1.x); vn1 = vn1.multiply(c.radius / vn1.length()); Point2DDouble vn2 = tangentsFromPoint[1].subtract(c.p); vn2 = new Point2DDouble(-vn2.y, vn2.x); vn2 = vn2.multiply(c.radius / vn2.length()); Point2DDouble[][] ret = new Point2DDouble[][] { {tangentsFromPoint[0].add(vn1), c.p.add(vn1)}, {tangentsFromPoint[1].add(vn2), c.p.add(vn2)} }; if (comp.compare(ret[0][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][0].distance(p), radius) != 0) throw new AssertionError(); if (comp.compare(ret[0][1].distance(c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(ret[1][1].distance(c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[0][0], ret[0][1], p), radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[0][0], ret[0][1], c.p), c.radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[1][0], ret[1][1], p), radius) != 0) throw new AssertionError(); if (comp.compare(GeometryAlgorithms.distanceToLine(ret[1][0], ret[1][1], c.p), c.radius) != 0) throw new AssertionError(); return ret; }
public boolean containsOn(Point2DDouble p, DoubleComparator comp) { return comp.compare(p.distanceSquared(this.p), radius * radius) == 0; }
public void solve(int testNumber, FastScanner in, FastPrinter out) { Circle2DDouble c = new Circle2DDouble(new Point2DDouble(in.nextInt(), in.nextInt()), in.nextInt()); Point2DDouble p1 = new Point2DDouble(in.nextInt(), in.nextInt()); Point2DDouble p2 = new Point2DDouble(in.nextInt(), in.nextInt()); if (p1.x > p2.x) { double t = p1.x; p1.x = p2.x; p2.x = t; } if (p1.y > p2.y) { double t = p1.y; p1.y = p2.y; p2.y = t; } List<Point2DDouble> allPoints = new ArrayList<>(); allPoints.add(p1); allPoints.add(p2); allPoints.add(new Point2DDouble(p1.x, p2.y)); allPoints.add(new Point2DDouble(p2.x, p1.y)); allPoints.add(new Point2DDouble(c.p.x - c.radius, c.p.y)); allPoints.add(new Point2DDouble(c.p.x + c.radius, c.p.y)); allPoints.add(new Point2DDouble(c.p.x, c.p.y - c.radius)); allPoints.add(new Point2DDouble(c.p.x, c.p.y + c.radius)); for (double x : new double[] {p1.x, p2.x}) { if (comp.compare(x, c.p.x - c.radius) > 0 && comp.compare(x, c.p.x + c.radius) < 0) { double dx = c.p.x - x; double dy = Math.sqrt(c.radius * c.radius - dx * dx); allPoints.add(new Point2DDouble(x, c.p.y - dy)); allPoints.add(new Point2DDouble(x, c.p.y + dy)); } } for (double y : new double[] {p1.y, p2.y}) { if (comp.compare(y, c.p.y - c.radius) > 0 && comp.compare(y, c.p.y + c.radius) < 0) { double dy = c.p.y - y; double dx = Math.sqrt(c.radius * c.radius - dy * dy); allPoints.add(new Point2DDouble(c.p.x - dx, y)); allPoints.add(new Point2DDouble(c.p.x + dx, y)); } } List<Point2DDouble> onRect = new ArrayList<>(); for (Point2DDouble e : allPoints) { if (inside(p1, p2, e)) { if (comp.compare(p1.x, e.x) < 0 && comp.compare(e.x, p2.x) < 0 && comp.compare(p1.y, e.y) < 0 && comp.compare(e.y, p2.y) < 0) { continue; } onRect.add(e); } } List<Point2DDouble> onCircle = new ArrayList<>(); for (Point2DDouble e : allPoints) { if (comp.compare(e.distance(c.p), c.radius) == 0) { onCircle.add(e); } } final Point2DDouble centerRect = p1.add(p2).multiply(0.5); Collections.sort( onRect, new Comparator<Point2DDouble>() { int get(Point2DDouble e) { int x = comp.compare(e.x, 0); int y = comp.compare(e.y, 0); if (x > 0) { return y > 0 ? 2 : y < 0 ? 8 : 1; } else if (x < 0) { return y > 0 ? 4 : y < 0 ? 6 : 5; } else { return y > 0 ? 3 : y < 0 ? 7 : 0; } } @Override public int compare(Point2DDouble o1, Point2DDouble o2) { o1 = o1.subtract(centerRect); o2 = o2.subtract(centerRect); int c = get(o1) - get(o2); if (c != 0) return c; return comp.compare(o2.vmul(o1), 0); } }); final Point2DDouble circleCenter = c.p; Collections.sort( onCircle, new Comparator<Point2DDouble>() { int get(Point2DDouble e) { int x = comp.compare(e.x, 0); int y = comp.compare(e.y, 0); if (x > 0) { return y > 0 ? 2 : y < 0 ? 8 : 1; } else if (x < 0) { return y > 0 ? 4 : y < 0 ? 6 : 5; } else { return y > 0 ? 3 : y < 0 ? 7 : 0; } } @Override public int compare(Point2DDouble o1, Point2DDouble o2) { o1 = o1.subtract(circleCenter); o2 = o2.subtract(circleCenter); int c = get(o1) - get(o2); if (c != 0) return c; return comp.compare(o2.vmul(o1), 0); } }); List<Segment> allSegments = new ArrayList<>(); for (int i = 0; i < onRect.size(); i++) { Point2DDouble r1 = onRect.get(i); Point2DDouble r2 = onRect.get((i + 1) % onRect.size()); if (pointsEqual(r1, r2)) continue; Point2DDouble r0 = r1.add(r2).multiply(0.5); if (comp.compare(r0.distance(c.p), c.radius) < 0) { allSegments.add(new Segment(r1, r2, 0)); } } for (int i = 0; i < onCircle.size(); i++) { Point2DDouble r1 = onCircle.get(i); Point2DDouble r2 = onCircle.get((i + 1) % onCircle.size()); if (pointsEqual(r1, r2)) continue; r1 = r1.subtract(c.p); r2 = r2.subtract(c.p); Point2DDouble r0 = r1.add(r2); r0 = r0.multiply(c.radius / r0.length()); r0 = r0.add(c.p); if (inside(p1, p2, r0)) { allSegments.add(new Segment(r1.add(c.p), r2.add(c.p), c.radius)); } } if (allSegments.isEmpty()) { out.println(0); return; } boolean[] was = new boolean[allSegments.size()]; List<Segment> sortedSegments = new ArrayList<>(); sortedSegments.add(allSegments.get(0)); was[0] = true; Point2DDouble last = sortedSegments.get(sortedSegments.size() - 1).q; // System.out.println(allSegments); while (!pointsEqual(sortedSegments.get(0).p, last)) { int id = -1; for (int i = 0; i < allSegments.size(); i++) { if (was[i]) continue; Segment e = allSegments.get(i); if (pointsEqual(e.p, last)) { id = i; break; } } if (id < 0) throw new AssertionError(); was[id] = true; sortedSegments.add(allSegments.get(id)); last = allSegments.get(id).q; } double area = 0; for (Segment e : sortedSegments) { area += e.p.vmul(e.q); } area = Math.abs(area) * .5; for (Segment e : sortedSegments) { if (comp.compare(e.r, 0) > 0) { double dist = e.p.distance(e.q); double sin = dist * .5 / c.radius; if (sin > 1) sin = 1; else if (sin < -1) sin = -1; double ang = Math.asin(sin) * 2; area += c.radius * c.radius * .5 * (ang - Math.sin(ang)); } } out.println(area); }
private boolean inside(Point2DDouble p1, Point2DDouble p2, Point2DDouble e) { return comp.compare(p1.x, e.x) <= 0 && comp.compare(e.x, p2.x) <= 0 && comp.compare(p1.y, e.y) <= 0 && comp.compare(e.y, p2.y) <= 0; }
static boolean pointsEqual(Point2DDouble p, Point2DDouble q) { return comp.compare(p.x, q.x) == 0 && comp.compare(p.y, q.y) == 0; }