def ram_horn3():
    center = meshutil.Transform().translate(-0.5, -0.5, 0)
    cage0 = cage.Cage.from_arrays([
        [0, 0, 0],
        [1, 0, 0],
        [1, 1, 0],
        [0, 1, 0],
    ]).transform(center)
    xf0_to_1 = meshutil.Transform().translate(0, 0, 1)
    cage1 = cage0.transform(xf0_to_1)
    opening_boundary = lambda i: meshutil.Transform() \
                                         .translate(0,0,-1) \
                                         .scale(0.5) \
                                         .translate(0.25,0.25,1) \
                                         .rotate([0,0,1], i*numpy.pi/2)
    incr = meshutil.Transform() \
                   .scale(0.9) \
                   .rotate([-1,0,1], 0.3) \
                   .translate(0,0,0.8)

    def recur(xf):
        while True:
            cage2 = cage1.transform(xf)
            yield cage2
            xf = incr.compose(xf)

    # TODO: I think there is a way to express 'recur' in the form of
    # itertools.accumulate, and it might be clearer. This function is
    # just iteratively re-composing 'incr' into a seed transformation,
    # and applying this transformation (at every stage) to the same
    # mesh.
    gens = [cage.CageGen(recur(opening_boundary(i))) for i in range(4)]
    cg = cage.CageGen(itertools.chain([cage0, cage1, cage.CageFork(gens)]))
    # TODO: if this is just a list it seems silly to require itertools
    mesh = cg.to_mesh(count=128, close_first=True, close_last=True)
    return mesh
def ram_horn_branch():
    center = meshutil.Transform().translate(-0.5, -0.5, 0)
    cage0 = cage.Cage.from_arrays([
        [0, 0, 0],
        [1, 0, 0],
        [1, 1, 0],
        [0, 1, 0],
    ]).transform(center)
    incr = meshutil.Transform() \
                   .scale(0.9) \
                   .rotate([-1,0,1], 0.3) \
                   .translate(0,0,0.8)

    def recur(xf, cage1, count):
        for i in range(count):
            if i > 0:
                c = cage1.transform(xf)
                yield c
            xf0 = xf
            xf = incr.compose(xf)

        def xf_sub(i):
            # (dx,dy) should be normalized, but I reused from something else
            dx = 1 if i == 0 or i == 1 else -1
            dy = 1 if i == 0 or i == 3 else -1
            return meshutil.Transform().translate(0, 0,
                                                  0.5).rotate([-dy, dx, 0],
                                                              -numpy.pi / 6)

        subdiv, trans_vs, trans_es = cage1.subdivide_deprecated()
        gens = [
            cage.CageGen(
                itertools.chain([cage_sub.transform(xf)],
                                recur(xf_sub(i).compose(xf), cage_sub, 8)))
            for i, cage_sub in enumerate(subdiv)
        ]
        yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)

    cg = cage.CageGen(
        itertools.chain(
            [cage0],
            recur(meshutil.Transform(), cage0, 8),
        ))
    # TODO: if this is just a list it seems silly to require itertools
    mesh = cg.to_mesh(count=32, close_first=True, close_last=True)
    return mesh
    def recur(xf, cage1, count):
        for i in range(count):
            if i > 0:
                c = cage1.transform(xf)
                yield c
            xf0 = xf
            xf = incr.compose(xf)

        def xf_rot(a):
            return meshutil.Transform().rotate([0, 1, 0], a)

        subdiv, trans_vs, trans_es = cage1.subdivide_x_deprecated()
        gens = [
            cage.CageGen(
                itertools.chain([cage_sub.transform(xf)],
                                recur(xf_rot(ang).compose(xf), cage_sub, 5)))
            for cage_sub, ang in zip(subdiv, [-0.2, 0.7])
        ]
        yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)
def dream_pendant():
    center = meshutil.Transform().translate(-0.5, -0.5, 0)
    cage0 = cage.Cage.from_arrays([
        [0, 0, 0],
        [1, 0, 0],
        [1, 1, 0],
        [0, 1, 0],
    ]).transform(center)
    incr = meshutil.Transform() \
                   .scale(0.95, 1.0, 0.95) \
                   .rotate([0,1,0], 0.2) \
                   .translate(0,0,0.9)

    def recur(xf, cage1, count):
        for i in range(count):
            if i > 0:
                c = cage1.transform(xf)
                yield c
            xf0 = xf
            xf = incr.compose(xf)

        def xf_rot(a):
            return meshutil.Transform().rotate([0, 1, 0], a)

        subdiv, trans_vs, trans_es = cage1.subdivide_x_deprecated()
        gens = [
            cage.CageGen(
                itertools.chain([cage_sub.transform(xf)],
                                recur(xf_rot(ang).compose(xf), cage_sub, 5)))
            for cage_sub, ang in zip(subdiv, [-0.2, 0.7])
        ]
        yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)

    cg = cage.CageGen(
        itertools.chain(
            [cage0],
            recur(meshutil.Transform(), cage0, 3),
        ))
    # TODO: if this is just a list it seems silly to require itertools
    mesh1 = cg.to_mesh(count=32, close_first=False, close_last=True)
    mesh2 = mesh1.transform(meshutil.Transform().rotate([0, 1, 0], math.pi))
    return meshutil.FaceVertexMesh.concat_many([mesh1, mesh2])
    def recur(xf, cage1, count):
        for i in range(count):
            if i > 0:
                c = cage1.transform(xf)
                yield c
            xf0 = xf
            xf = incr.compose(xf)

        def xf_sub(i):
            # (dx,dy) should be normalized, but I reused from something else
            dx = 1 if i == 0 or i == 1 else -1
            dy = 1 if i == 0 or i == 3 else -1
            return meshutil.Transform().translate(0, 0,
                                                  0.5).rotate([-dy, dx, 0],
                                                              -numpy.pi / 6)

        subdiv, trans_vs, trans_es = cage1.subdivide_deprecated()
        gens = [
            cage.CageGen(
                itertools.chain([cage_sub.transform(xf)],
                                recur(xf_sub(i).compose(xf), cage_sub, 8)))
            for i, cage_sub in enumerate(subdiv)
        ]
        yield cage.CageFork(gens, xf.apply_to(trans_vs), trans_es)