示例#1
0
    def setUp(self):
        data = [[0,0], [-0.5, -0.5], [-0.5, 0.5], [0.5, -0.5], [0.5, 0.5]]
        self.delau = DelaunayTri(data)
        sphere_data = [[0.3927286959385721, 0.3027233106882571,
                        -0.0642087887467873],
                       [-0.3040289937812381, 0.08411211324060132,
                        -0.3879323695524365],
                       [-0.4167147320140305, -0.1124203247935928,
                        0.252409395022804],
                       [-0.09784613055257477, 0.3994051836806832,
                        0.2844321254445218],
                       [0.0167085276338464, 0.4969839143091518,
                        0.05222847903455247],
                       [-0.3010383814570601, 0.3973744439739354,
                        0.03833332970300512],
                       [0.321916886905792, -0.3514017778986294,
                        -0.1512822144687402],
                       [-0.02357104947939958, 0.4654006301246191,
                        -0.1812364728912125],
                       [0.3199720537828011, -0.3299443654301472,
                        -0.1968618818332127],
                       [-0.4630278928730662, -0.1886147011806086,
                        0.005446551209538857]]
        self.spdelau = DelaunayTri(sphere_data)

        #Make sure higher dim works.
        points = np.random.randn(10, 5)
        self.hddelau = DelaunayTri(points)

        #Check that bad points raises an error.
        bad_points = [[0,0], [0,1,0], [0,0]]
        self.assertRaises(ValueError, DelaunayTri, bad_points)
示例#2
0
def section1(fname='./data/elephant.pwn', K=5, ndisc=50):
    # read in data (N x 3)
    print('reading data...')
    data = read_file(fname)

    # discretize volume (XYZ x 3) and voxel size (3)
    print('voxel grid...')
    xyzpts, dx = get_grid(data, ndisc=ndisc)

    # calculate mean distance to K nearest neighbors
    print('calculate distance...')
    neigh = NearestNeighbors(n_neighbors=K).fit(data.numpy())
    dist, _ = neigh.kneighbors(xyzpts.numpy(), return_distance=True)
    dist = torch.Tensor(dist).mean(1)

    # filter out points that are "far" from the point cloud
    kept = dist < np.linalg.norm(dx)
    xyzpts = xyzpts[kept]
    dist = dist[kept]

    # delaunay triangulation
    print('delaunay...')
    M = DelaunayTri(xyzpts.numpy())
    points = torch.Tensor(M.points)
    tetvertices = torch.LongTensor(M.vertices)
    trivertices = get_trivertices(tetvertices)

    # choose epsilon
    print('choosing epsilon...')
    eixes = dist.sort().values[torch.linspace(0, len(dist) - 1, 50).long()]
    epsilon = eixes[int(len(eixes) * 0.5)]
    is_in_band = dist < epsilon

    return points, tetvertices, trivertices, is_in_band, epsilon, dist
示例#3
0
def alpha_shape(points, alpha):
    def add_edge(points, i, j):
        if (i, j) in edges or (j, i) in edges:
            return
        edges.add((i, j))
        edge_points.append((points[i], points[j]))

    tri = DelaunayTri(points)
    edges = set()
    edge_points = []
    for i1, i2, i3 in tri.vertices:
        x1, y1 = tri.points[i1]
        x2, y2 = tri.points[i2]
        x3, y3 = tri.points[i3]
        a = hypot(x1 - x2, y1 - y2)
        b = hypot(x2 - x3, y2 - y3)
        c = hypot(x3 - x1, y3 - y1)
        s = (a + b + c) / 2.0
        area = sqrt(s * (s - a) * (s - b) * (s - c))
        radius = a * b * c / (4 * area)
        if radius < 1.0 / alpha:
            add_edge(tri.points, i1, i2)
            add_edge(tri.points, i2, i3)
            add_edge(tri.points, i3, i1)
    shape = cascaded_union(list(polygonize(MultiLineString(edge_points))))
    return shape
示例#4
0
 def __init__(self, diameter, density):
     self.fmt = GeomVertexFormat.getV3c4()
     self.diameter = diameter
     r = diameter / 2
     # Sample random points in a 2d plane
     points_2d = []
     for i in range(int(density * pi * r**2)):
         x, y = random_in_circle()
         points_2d.append((x * r, y * r))
     # Compute triangulations
     triangulation = DelaunayTri(points_2d)
     # Save points in 3d space
     self.points = []
     for x, y in triangulation.points:
         p = Vec3(x, y, elevation(x, y))
         self.points.append(p)
     # Save triangles
     self.vertices = triangulation.vertices
     # Save a graph with the triangulation
     self.nodes = [set() for i in range(len(self.points))]
     self.edges = []
     for a, b, c in triangulation.vertices:
         self.nodes[a] |= {b, c}
         self.nodes[b] |= {a, c}
         self.nodes[c] |= {a, b}
         self.edges.append((a, b))
         self.edges.append((b, c))
         self.edges.append((c, a))
示例#5
0
def face_swap(img1, img2, points1, points2):
    img1Warped = np.copy(img2)

    # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
    # Find convex hull
    hull1 = []
    hull2 = []

    # hullIndex = cv2.convexHull(np.array(points2), returnPoints=False)
    hullIndex = ConvexHull(np.array(points2)).vertices

    for i in range(0, len(hullIndex)):
        hull1.append(points1[hullIndex[i][0]])
        hull2.append(points2[hullIndex[i][0]])

    # Find delanauy traingulation for convex hull points
    sizeImg2 = img2.shape
    rect = (0, 0, sizeImg2[1], sizeImg2[0])

    # dt = fbc.calculateDelaunayTriangles(rect, hull2)
    dt = DelaunayTri(hull2, True).vertices
    # dt = Dela

    if len(dt) == 0:
        quit()

    # Apply affine transformation to Delaunay triangles
    for i in range(0, len(dt)):
        t1 = []
        t2 = []

        #get points for img1, img2 corresponding to the triangles
        for j in range(0, 3):
            t1.append(hull1[dt[i][j]])
            t2.append(hull2[dt[i][j]])

        fbc.warpTriangle(img1, img1Warped, t1, t2)

    # Calculate Mask for Seamless cloning
    hull8U = []
    for i in range(0, len(hull2)):
        hull8U.append((hull2[i][0], hull2[i][1]))

    mask = np.zeros(img2.shape, dtype=img2.dtype)

    cv2.fillConvexPoly(mask, np.int32(hull8U), (255, 255, 255))
    # find center of the mask to be cloned with the destination image
    r = cv2.boundingRect(np.float32([hull2]))

    center = ((r[0] + int(r[2] / 2), r[1] + int(r[3] / 2)))

    # img2 = cv2.cvtColor(img2, cv2.COLOR_RGB2GRAY)

    # Clone seamlessly.
    img2 = cv2.seamlessClone(np.uint8(img1Warped), img2, mask, center,
                             cv2.NORMAL_CLONE)

    return img2
示例#6
0
    def construct_model(self):
        # build model w/o runtime
        model_points_features = [[f1, f2]
                                 for [[f1, f2], f3] in self.model_points]
        self.log_debug("construct_model point count: " +
                       str(len(self.model_points)))
        self.log_debug("Constructing model with points: " +
                       str(self.model_points))

        # do initial triangulation with model_points
        # refer: https://pythonhosted.org/pyhull/
        self.tri = DelaunayTri(model_points_features)
示例#7
0
def _concave(points, alpha):
    if len(points) <= 3:
        return points

    # Create delaunay triangulation.
    tri = DelaunayTri(points)

    # For each edge, count the number of faces that reference it.
    edge_counts = Counter()

    for (i, j, k) in tri.vertices:
        d1 = dist(tri.points[i], tri.points[j]) < alpha
        d2 = dist(tri.points[i], tri.points[k]) < alpha
        d3 = dist(tri.points[j], tri.points[k]) < alpha
        if (d1) and (d2) and (d3):
            edge_counts[tuple(sorted((i, j)))] += 1
            edge_counts[tuple(sorted((i, k)))] += 1
            edge_counts[tuple(sorted((j, k)))] += 1

    perim_edges = defaultdict(list)  # Store vertices on perimeter.
    for (i, j), count in edge_counts.items():
        # If edge has one reference, it is on the perimeter.
        if count == 1:
            perim_edges[i].append(j)
            perim_edges[j].append(i)

    for i, vl in perim_edges.items():
        if len(vl) != 2:
            raise InvalidHullException()

    start_v = list(perim_edges.keys())[0]
    ordered_verts = [start_v]
    next_v = perim_edges[start_v][0]
    prev_v = start_v

    while next_v != start_v:
        ordered_verts.append(next_v)
        if perim_edges[next_v][0] != prev_v:
            prev_v = next_v
            next_v = perim_edges[next_v][0]
        else:
            prev_v = next_v
            next_v = perim_edges[next_v][1]
        perim_edges[prev_v].remove(next_v)
    return ordered_verts, (tri, edge_counts)
示例#8
0
文件: state_diff.py 项目: afcarl/tmdp
def resample(mdp, solve_result, num_points):
    policy = solve_result['policy']
    value = solve_result['value']
    tension = get_tension_matrix(mdp, policy, value)
    density = {a: -b for (a, b) in tension.items()}
    density_sf = sf_from_tension(density, alpha=1.5)

    support_points = mdp.get_support_points()

    shape = mdp.get_grid().get_map().shape
    grid = mdp.get_grid()

    def support():
        x = np.random.rand(2)
        return np.array([x[0] * shape[0], x[1] * shape[1]])

    def weight(p):
        cell = np.floor(p)
        cell = (int(cell[0]), int(cell[1]))
        empty = grid.is_empty(cell)
        if not empty:
            return 0
        d = density_sf.query(p)
        return d

    sampled_points = rejection_sampling(support=support,
                                        weight=weight,
                                        N=num_points)

    points = support_points + sampled_points
    from pyhull.delaunay import DelaunayTri
    delaunay_tri = DelaunayTri(points)
    print delaunay_tri.vertices
    print delaunay_tri.points

    solve_result['delaunay_tri'] = delaunay_tri
    solve_result['sampled_points'] = sampled_points
    solve_result['tension'] = tension
    solve_result['density_sf'] = density_sf
    solve_result['support_points'] = support_points
    return solve_result
示例#9
0
legkeys = []
legitems = []

if dim == 3:
    from mpl_toolkits.mplot3d import Axes3D
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

for pt in points:
    p, = plt.plot(pt[0], pt[1], 'ro') if dim == 2 \
        else plt.plot([pt[0]], [pt[1]], [pt[2]], 'ro')

legkeys.append(p)
legitems.append("Points")

d = DelaunayTri(points)

for s in d.simplices:
    for data in itertools.combinations(s.coords, dim):
        data = np.array(data)
        p, = plt.plot(data[:,0], data[:,1], 'g-') if dim == 2 else\
            ax.plot(data[:,0], data[:,1], data[:,2], 'g-')

legkeys.append(p)
legitems.append("Delaunay tri")

d = ConvexHull(points)

for s in d.simplices:
    for data in itertools.combinations(s.coords, dim):
        data = np.array(data)
示例#10
0
#	y *= 3000
#	vertices.append((x, y))
side = 200
vertices = list()
imax = round(2000 / (side * math.sqrt(3) / 2))
jmax = round(3000 / side)
side = 2000 / imax / (math.sqrt(3) / 2)
for i in range(imax):
	for j in range(-1, jmax+1):
		x = side * (i * math.sqrt(3) / 2 + 0.5)
		y = side * (j + 0.5 * (i % 2)) + 1500 % side
		if y > 0 and y < 3000:
			vertices.append((x, y))

# Generate graph edges
delaunay= DelaunayTri(vertices,joggle=True)
edges = set()
for i, j, k in delaunay.vertices:
	edges.add((min(i, j), max(i, j)))
	edges.add((min(j, k), max(j, k)))
	edges.add((min(k, i), max(k, i)))

# Add vertices to the ggb file
for i, vertex in enumerate(vertices):
	element = ElementTree.SubElement(construction, 'element', type='point', label=vertex_name(i))
	ElementTree.SubElement(element, 'coords', x=str(vertex[0]), y=str(vertex[1]), z='1')
	ElementTree.SubElement(element, 'show', object='true', label='false')
	ElementTree.SubElement(element, 'condition', showObject='ShowRoadmap')

# Add edges to the ggb file
for i, j in edges:
示例#11
0



dim =2


#for pt in b:
 #   p, = plt.plot(pt[0], pt[1], 'ro')
      





d = DelaunayTri(b)
del b
for s in d.simplices:
   for data in itertools.combinations(s.coords, dim):
        data = np.array(data)
        p, = plt.plot(data[:,0], data[:,1], 'g-')






for s in d.simplices:
    for data in itertools.combinations(s.coords, dim):
        data = np.array(data)
        p, = plt.plot(data[:,0], data[:,1], 'b-')
示例#12
0
 def test_joggle(self):
     joggled_delau = DelaunayTri(self.delau.points, joggle=True)
     expected_ans = set([(0, 1, 3), (0, 1, 2), (0, 3, 4), (0, 2, 4)])
     ans = set([tuple(sorted(x)) for x in joggled_delau.vertices])
     self.assertEqual(ans, expected_ans)
def grid_interpolating(defaultPose, minimalPose, maximalPose, workerTarget):
    print('defaultPose: ', defaultPose)

    nx = 20  #50
    ny = 15  #60
    nz = 5
    nxj = 20j
    nyj = 15j
    nzj = 10j

    #points = np.random.rand(8, 3)*10
    points = np.array([[-10, -10, -10], [10, 10, 10], [0, 0,
                                                       0], [-10, -10, 10],
                       [-10, 10, -10], [-10, 10, 10], [10, -10, -10],
                       [10, 10, -10], [0, 0, 0], [10, -10, -10], [10, -10, 10],
                       [10, 10, -10]])
    points = np.array([[-10, -10, -10], [-10, -10, 10], [-10, 10, -10],
                       [10, -10, -10], [0, 0, 0], [-5, -5, -5], [5, 5, 5],
                       [0, 5, 5], [5, 5, -5], [5, -5, -5], [-5, 5, -5],
                       [10, 10, 10], [10, 5, 10]])
    #points = np.array([[-10, -10, -10], [-10, -10, 10], [-10, 10, -10], [10, -10, -10]])
    values = f(points[:, 0], points[:, 1], points[:, 2], workerTarget)
    print('points: ', points)
    print('values; ', values)

    grid_x, grid_y, grid_z = np.mgrid[minimalPose[0]:maximalPose[0]:nxj,
                                      minimalPose[1]:maximalPose[1]:nyj,
                                      minimalPose[2]:maximalPose[2]:nzj]

    gridDist = griddata(points,
                        values, (grid_x, grid_y, grid_z),
                        method='linear')

    plt.close('all')
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.scatter(workerTarget[0],
               workerTarget[1],
               workerTarget[2],
               c='r',
               marker='x')

    if False:
        tri = DelaunayTri(points, joggle='Qz')
        shape = np.shape(grid_x)
        grid_xr = np.reshape(grid_x, -1)
        grid_yr = np.reshape(grid_y, -1)
        grid_zr = np.reshape(grid_z, -1)
        grid_z2 = np.empty(len(grid_zr))
        grid_z2.fill(-1)
        print(grid_z2)
        for simplex in tri.simplices:
            print('points: ', simplex.coords)
            simplexValues = np.zeros(4)
            for idxV, vertex in enumerate(simplex.coords):
                for idx, point in enumerate(points):
                    if np.all(point == vertex):
                        simplexValues[idxV] = values[idx]
            for idx in range(0, len(grid_xr)):
                if simplex.in_simplex(
                    [grid_xr[idx], grid_yr[idx], grid_zr[idx]]):
                    grid_z2[idx] = np.dot(
                        simplexValues,
                        np.transpose(
                            simplex.bary_coords(
                                [grid_xr[idx], grid_yr[idx], grid_zr[idx]])))
        grid_z2 = np.where(grid_z2 == -1, np.nan, grid_z2)
        grid_z2 = np.reshape(grid_z2, shape)
        #ax.plot_trisurf(points[:,0], points[:,1],  points[:,2], triangles=tri.vertices.copy())

    ax.plot(points[:, 0], points[:, 1], points[:, 2], 'o')

    x = np.linspace(minimalPose[0], maximalPose[0], nx)
    y = np.linspace(minimalPose[1], maximalPose[1], ny)
    Y, X = np.meshgrid(y, x)

    norm = mpl.colors.Normalize(vmin=np.nanmin(gridDist),
                                vmax=np.nanmax(gridDist))
    print('m: ', np.nanmin(gridDist), ' M: ', np.nanmax(gridDist))
    #ebenen
    #for index_z, z in enumerate(np.linspace(minimalPose[2], maximalPose[2], nz)):
    #cset = ax.contourf(X, Y, gridDist[:, :, int(np.round(index_z*nzj*-1j/nz))], zdir='z', offset=z, cmap=cm.jet, norm=norm)

    #points
    boxMarkerN = ax.scatter(grid_x.flatten(),
                            grid_y.flatten(),
                            grid_z.flatten(),
                            marker='o',
                            c=gridDist.flatten(),
                            s=20)
    #plt.colorbar(cset, orientation='vertical', extend='both...')

    # Tweak the limits and add latex math labels.
    #ax.set_zlim(0, 1)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')

    plt.show()
示例#14
0
def tessellation_file(input_dir, input_file, cut_distance, output_dir):
    """
    Write tessellation file based on pdb_codon_dssp files, all simplex whose edges greater than distance_cut will be
    removed.
    """
    f = list(
        open(os.path.join(input_dir, '%s_pdb_codon_dssp' % input_file), 'r'))
    g = open(os.path.join(output_dir, "%s_tessellation" % input_file[:5]), 'a')

    codon_list = []
    residue_num = []
    coordinates = []
    dssp_list = []

    # Extract codon, residue number, coordinates, dssp info.
    for i in range(len(f)):
        codon = f[i][:2]
        res = [m.split('\t', 3)[2] for m in [f[i][:-1]]][0]
        cor_x = float([m.split('\t', 4)[3] for m in [f[i][:-1]]][0])
        cor_y = float([m.split('\t', 5)[4] for m in [f[i][:-1]]][0])
        cor_z = float([m.split('\t', 6)[5] for m in [f[i][:-1]]][0])
        dssp = [m.split('\t', 6)[6] for m in [f[i][:-1]]][0]

        codon_list.append(codon)
        coordinates.append([cor_x, cor_y, cor_z])
        residue_num.append(res)
        dssp_list.append(dssp)

    # Perform Delaunay Tessellation using pyhull, inputs are coordinates.
    tri = DelaunayTri(coordinates)

    # Sort DelaunayTri.vertices inside list first, then entire list.
    sort_tri_vertices = []
    for i in range(len(tri.vertices)):
        cache0_tri = sorted(tri.vertices[i])
        sort_tri_vertices.append(cache0_tri)
    sort_tri_vertices = sorted(sort_tri_vertices)

    for i in range(len(sort_tri_vertices)):
        cache_tri = sort_tri_vertices[i]

        # Four neighbor codons as a simplex.
        simplex = codon_list[cache_tri[0]] + codon_list[
            cache_tri[1]] + codon_list[cache_tri[2]] + codon_list[cache_tri[3]]

        # Rewrite simplex based on alphabet.
        simplex_sort = sorted([
            codon_list[cache_tri[0]], codon_list[cache_tri[1]],
            codon_list[cache_tri[2]], codon_list[cache_tri[3]]
        ])
        sorted_simplex = simplex_sort[0] + simplex_sort[1] + simplex_sort[
            2] + simplex_sort[3]

        # Write residue number based on simplex (not sorted simplex).
        r1 = residue_num[cache_tri[0]]
        r2 = residue_num[cache_tri[1]]
        r3 = residue_num[cache_tri[2]]
        r4 = residue_num[cache_tri[3]]

        # Calculate residue distance.
        residue_distance = int(r4) - int(r1) - 3
        cor1 = coordinates[cache_tri[0]]
        cor2 = coordinates[cache_tri[1]]
        cor3 = coordinates[cache_tri[2]]
        cor4 = coordinates[cache_tri[3]]

        # Calculate tetrahedron edges length, volume and tetrahedrality.
        l1, l2, l3, l4, l5, l6, volume, tetrahedrality = tetrahedron_calculation(
            cor1, cor2, cor3, cor4)

        # Write dssp based on simplex (not sorted simplex).
        ss1 = dssp_list[cache_tri[0]]
        ss2 = dssp_list[cache_tri[1]]
        ss3 = dssp_list[cache_tri[2]]
        ss4 = dssp_list[cache_tri[3]]

        edge_list = [l1, l2, l3, l4, l5, l6]
        edge_list = [float(j) for j in edge_list]

        # Check if all edge length less or equal to 12 Angstrom.
        # Write simplex, sorted_simplex, residue numbers, residue distance, edges length, volume, tetrahedrality, dssp.
        if all(edge <= cut_distance for edge in edge_list):
            g.writelines(simplex + '\t' + sorted_simplex + '\t' + r1 + '\t' +
                         r2 + '\t' + r3 + '\t' + r4 + '\t' +
                         str(residue_distance) + '\t' + l1 + '\t' + l2 + '\t' +
                         l3 + '\t' + l4 + '\t' + l5 + '\t' + l6 + '\t' +
                         volume + '\t' + tetrahedrality + '\t' + ss1 + '\t' +
                         ss2 + '\t' + ss3 + '\t' + ss4 + '\n')

    g.close()
示例#15
0
def face_average(img1, img2, points1, points2):
    images = []
    allPoints = []
    allPoints.append(points1)
    allPoints.append(points2)
    images.append(np.float32(img1) / 255.0)
    images.append(np.float32(img2) / 255.0)

    w = 300
    h = 300

    boundaryPts = fbc.getEightBoundaryPoints(h, w)

    numImages = len(images)
    numLandmarks = len(allPoints[0])

    imagesNorm = []
    pointsNorm = []

    pointsAvg = np.zeros((numLandmarks, 2), dtype=np.float32)
    # Warp images and trasnform landmarks to output coordinate system,
    # and find average of transformed landmarks.
    for i, img in enumerate(images):
        points = allPoints[i]
        points = np.array(points)

        img, points = fbc.normalizeImagesAndLandmarks((h, w), img, points)

        # Calculate average landmark locations
        pointsAvg = pointsAvg + (points / (1.0 * numImages))

        # Append boundary points. Will be used in Delaunay Triangulation
        points = np.concatenate((points, boundaryPts), axis=0)

        pointsNorm.append(points)
        imagesNorm.append(img)

    # Append boundary points to average points.
    pointsAvg = np.concatenate((pointsAvg, boundaryPts), axis=0)

    # Delaunay triangulation
    rect = (0, 0, w, h)
    # dt = fbc.calculateDelaunayTriangles(rect, pointsAvg)
    dt = DelaunayTri(pointsAvg, True).vertices

    # Output image
    output = np.zeros((h, w, 3), dtype=np.float)

    # Warp input images to average image landmarks
    for i in range(0, numImages):

        imWarp = fbc.warpImage(imagesNorm[i], pointsNorm[i],
                               pointsAvg.tolist(), dt)

        # Add image intensities for averaging
        output = output + imWarp

    # Divide by numImages to get average
    output = output / (1.0 * numImages)
    output = output * 255.0
    output = np.uint8(output)
    # print(output)
    return output
示例#16
0
    def make(self, prod_svg, debug_svg):
        x0, y0 = self.x0, self.y0
        x1, y1 = self.x1, self.y1

        b0 = (x1 - x0) / 2 - (6.125 + 7.5)
        block = Polygon.Polygon([
            (x0 + b0, y0 + 0),
            (x1 - b0, y0 + 0),
            (x1 - b0, y0 + 14.25 + 7.5),
            (x0 + b0, y0 + 14.25 + 7.5),
        ])

        b1 = (x1 - x0) / 2 - 6.125
        tunnel = Polygon.Polygon([
            (x0 + b1, y0 + -10),
            (x1 - b1, y0 + -10),
            (x1 - b1, y0 + 14.25),
            (x0 + b1, y0 + 14.25),
        ])

        width = (x1 - x0) + 150
        height = (y1 - y0) + 150
        points = poisson_disc_samples(width=width, height=height, r=self.cfg.r)

        points = [(p[0] + x0 - 75, y1 - p[1] + 75) for p in points]

        # for p in points:
        #     prod_svg.add(prod_svg.circle(p, 0.5, fill="green"))

        dely = DelaunayTri(points)
        cells = spooky_cells(dely)

        wx0 = min(p[0] for p in self.window[0])
        wx1 = max(p[0] for p in self.window[0])

        wy0 = min(p[1] for p in self.window[0])
        wy1 = max(p[1] for p in self.window[0])

        g0 = Graph(prod_svg, (wx0, wx1), (10, 10 + 30), (0.0, 1.0), "darkblue")
        g1 = Graph(prod_svg, (wx0, wx1), (10 + 30 + 5, 10 + 30 + 5 + 30), (0.0, 1.0), "darkgreen")

        for i, cell in sorted(cells.items()):
            inbounds = all(x0 - 50 < x < x1 + 50 and y0 - 50 < y < y1 + 50 for (x, y) in cell)
            if not inbounds:
                continue

            fixed = make_convex(cell)

            cx, cy = centroid(fixed)

            t = cx / (wx1 - wx0)
            s = cy / (wy1 - wy0)

            v = 0.73 * (
                    0.5
                    + noise.snoise2(0.5 * s, 0.25 * t)
                    + 0.5 * noise.snoise2(1 * s, 0.5 * t + 100)
                    + 0.25 * noise.snoise2(2 * s, 1 * t + 200)
            )
            v = geom.clamp(0, v, 1)
            g0.plot(cx, v)

            boost = inset_boost(v)
            g1.plot(cx, boost)

            inset_amount = self.cfg.line_width / 2.0 + boost

            p = inset(fixed, inset_amount)
            if p is None:
                continue

            cell = Polygon.Polygon(p)

            if len(cell & self.wall) == 0:
                continue

            c = centroid(cell.contour(0))
            a = cell.area(0)
            r = (a / pi) ** 0.5

            circle = Polygon.Polygon([p.point2 for p in circle_points(Vec(*c), r, 20)])

            debug_svg.add(debug_svg.polygon(cell.contour(0), fill_opacity=0, stroke="black", stroke_width=0.25))
            debug_svg.add(debug_svg.circle(c, r, fill_opacity=0, stroke="black", stroke_width=0.25))
            # svg.save()

            new = geom.interpolate_poly_circle(debug_svg, cell.contour(0), c, r, 1 - v)
            new = Polygon.Polygon([p.point2 for p in new])

            debug_svg.add(debug_svg.polygon(new.contour(0), fill_opacity=0, stroke="orange", stroke_width=0.25))

            cell &= self.window
            circle &= self.window
            new &= self.window

            self.result += new

        debug_svg.add(debug_svg.polygon(self.window.contour(0), fill_opacity=0, stroke="black", stroke_width=0.25))
        debug_svg.add(debug_svg.polygon(self.wall.contour(0), fill_opacity=0, stroke="black", stroke_width=0.25))

        self.result = self.wall - self.result