def compute_venn2_regions(centers, radii): ''' Returns a triple of VennRegion objects, describing the three regions of the diagram, corresponding to sets (Ab, aB, AB) >>> centers, radii = solve_venn2_circles((1, 1, 0.5)) >>> regions = compute_venn2_regions(centers, radii) ''' A = VennCircleRegion(centers[0], radii[0]) B = VennCircleRegion(centers[1], radii[1]) Ab, AB = A.subtract_and_intersect_circle(B.center, B.radius) aB, _ = B.subtract_and_intersect_circle(A.center, A.radius) return (Ab, aB, AB)
def compute_venn3_regions(centers, radii): ''' Given the 3x2 matrix with circle center coordinates, and a 3-element list (or array) with circle radii [as returned from solve_venn3_circles], returns the 7 regions, comprising the venn diagram, as VennRegion objects. Regions are returned in order (Abc, aBc, ABc, abC, AbC, aBC, ABC) >>> centers, radii = solve_venn3_circles((1, 1, 1, 1, 1, 1, 1)) >>> regions = compute_venn3_regions(centers, radii) ''' A = VennCircleRegion(centers[0], radii[0]) B = VennCircleRegion(centers[1], radii[1]) C = VennCircleRegion(centers[2], radii[2]) Ab, AB = A.subtract_and_intersect_circle(B.center, B.radius) ABc, ABC = AB.subtract_and_intersect_circle(C.center, C.radius) Abc, AbC = Ab.subtract_and_intersect_circle(C.center, C.radius) aB, _ = B.subtract_and_intersect_circle(A.center, A.radius) aBc, aBC = aB.subtract_and_intersect_circle(C.center, C.radius) aC, _ = C.subtract_and_intersect_circle(A.center, A.radius) abC, _ = aC.subtract_and_intersect_circle(B.center, B.radius) return [Abc, aBc, ABc, abC, AbC, aBC, ABC]
def compute_venn2_regions(centers, radii): """ Returns a triple of VennRegion objects, describing the three regions of the diagram, corresponding to sets (Ab, aB, AB) >>> centers, radii = solve_venn2_circles((1, 1, 0.5)) >>> regions = compute_venn2_regions(centers, radii) """ A = VennCircleRegion(centers[0], radii[0]) B = VennCircleRegion(centers[1], radii[1]) Ab, AB = A.subtract_and_intersect_circle(B.center, B.radius) aB, _ = B.subtract_and_intersect_circle(A.center, A.radius) return Ab, aB, AB
def test_circle_region(): with pytest.raises(VennRegionException): vcr = VennCircleRegion((0, 0), -1) vcr = VennCircleRegion((0, 0), 10) assert abs(vcr.size() - np.pi*100) <= tol # Interact with non-intersecting circle sr, ir = vcr.subtract_and_intersect_circle((11, 1), 1) assert sr == vcr assert ir.is_empty() # Interact with self sr, ir = vcr.subtract_and_intersect_circle((0, 0), 10) assert sr.is_empty() assert ir == vcr # Interact with a circle that makes a hole with pytest.raises(VennRegionException): sr, ir = vcr.subtract_and_intersect_circle((0, 8.9), 1) # Interact with a circle that touches the side from the inside for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * (10 - r) cy = np.sin(a * np.pi / 180.0) * (10 - r) #print "Next test case", a, r, cx, cy, r TEST_TOLERANCE = tol if r > 0.001 and r < 9.999 else 1e-4 # For tricky circles the numeric errors for arc lengths are just too big here sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 for a in sr.arcs: assert abs(a.length_degrees() - 360) < TEST_TOLERANCE assert abs(ir.arcs[0].length_degrees() - 0) < TEST_TOLERANCE assert abs(ir.arcs[1].length_degrees() - 360) < TEST_TOLERANCE assert abs(sr.size() + np.pi*r**2 - vcr.size()) < tol assert abs(ir.size() - np.pi*r**2) < tol # Interact with a circle that touches the side from the outside for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * (10 + r) cy = np.sin(a * np.pi / 180.0) * (10 + r) sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) # Depending on numeric roundoff we may get either an self and VennEmptyRegion or two arc regions. In any case the sizes should match assert abs(sr.size() + ir.size() - vcr.size()) < tol if (sr == vcr): assert ir.is_empty() else: sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 assert abs(sr.arcs[0].length_degrees() - 0) < tol assert abs(sr.arcs[1].length_degrees() - 360) < tol assert abs(ir.arcs[0].length_degrees() - 0) < tol assert abs(ir.arcs[1].length_degrees() - 0) < tol # Interact with some cases of intersecting circles for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * 10 cy = np.sin(a * np.pi / 180.0) * 10 sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 assert abs(sr.size() + ir.size() - vcr.size()) < tol assert sr.size() > 0 assert ir.size() > 0 # Do intersection the other way vcr2 = VennCircleRegion([cx, cy], r) sr2, ir2 = vcr2.subtract_and_intersect_circle(vcr.center, vcr.radius) sr2.verify() ir2.verify() assert len(sr2.arcs) == 2 and len(ir2.arcs) == 2 assert abs(sr2.size() + ir2.size() - vcr2.size()) < tol assert sr2.size() > 0 assert ir2.size() > 0 for i in range(2): assert ir.arcs[i].approximately_equal(ir.arcs[i])
def test_circle_region(): with pytest.raises(VennRegionException): vcr = VennCircleRegion((0, 0), -1) vcr = VennCircleRegion((0, 0), 10) assert abs(vcr.size() - np.pi * 100) <= tol # Interact with non-intersecting circle sr, ir = vcr.subtract_and_intersect_circle((11, 1), 1) assert sr == vcr assert ir.is_empty() # Interact with self sr, ir = vcr.subtract_and_intersect_circle((0, 0), 10) assert sr.is_empty() assert ir == vcr # Interact with a circle that makes a hole with pytest.raises(VennRegionException): sr, ir = vcr.subtract_and_intersect_circle((0, 8.9), 1) # Interact with a circle that touches the side from the inside for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * (10 - r) cy = np.sin(a * np.pi / 180.0) * (10 - r) #print "Next test case", a, r, cx, cy, r TEST_TOLERANCE = tol if r > 0.001 and r < 9.999 else 1e-4 # For tricky circles the numeric errors for arc lengths are just too big here sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 for a in sr.arcs: assert abs(a.length_degrees() - 360) < TEST_TOLERANCE assert abs(ir.arcs[0].length_degrees() - 0) < TEST_TOLERANCE assert abs(ir.arcs[1].length_degrees() - 360) < TEST_TOLERANCE assert abs(sr.size() + np.pi * r**2 - vcr.size()) < tol assert abs(ir.size() - np.pi * r**2) < tol # Interact with a circle that touches the side from the outside for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * (10 + r) cy = np.sin(a * np.pi / 180.0) * (10 + r) sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) # Depending on numeric roundoff we may get either an self and VennEmptyRegion or two arc regions. In any case the sizes should match assert abs(sr.size() + ir.size() - vcr.size()) < tol if (sr == vcr): assert ir.is_empty() else: sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 assert abs(sr.arcs[0].length_degrees() - 0) < tol assert abs(sr.arcs[1].length_degrees() - 360) < tol assert abs(ir.arcs[0].length_degrees() - 0) < tol assert abs(ir.arcs[1].length_degrees() - 0) < tol # Interact with some cases of intersecting circles for (a, r) in [(0, 1), (90, 2), (180, 3), (290, 0.01), (42, 9.99), (-0.1, 9.999), (180.1, 0.001)]: cx = np.cos(a * np.pi / 180.0) * 10 cy = np.sin(a * np.pi / 180.0) * 10 sr, ir = vcr.subtract_and_intersect_circle((cx, cy), r) sr.verify() ir.verify() assert len(sr.arcs) == 2 and len(ir.arcs) == 2 assert abs(sr.size() + ir.size() - vcr.size()) < tol assert sr.size() > 0 assert ir.size() > 0 # Do intersection the other way vcr2 = VennCircleRegion([cx, cy], r) sr2, ir2 = vcr2.subtract_and_intersect_circle(vcr.center, vcr.radius) sr2.verify() ir2.verify() assert len(sr2.arcs) == 2 and len(ir2.arcs) == 2 assert abs(sr2.size() + ir2.size() - vcr2.size()) < tol assert sr2.size() > 0 assert ir2.size() > 0 for i in range(2): assert ir.arcs[i].approximately_equal(ir.arcs[i])