# Kambi: Hilbert curve generator (adds hilbert curve, as a mesh object,
# to the scene). At the end we also test non-interactive scene writing.
# You can run this from command-line like
#
#   blender --background --factory-startup --python make_hilbert_curve.py
#
# See blender example background_job.py for more useful tricks,
# like clearing existing objects first, parsing custom command-line params,
# and making also render.

import bpy

class HilbertCurve:
    '''Hilbert curve generator, independent from Blender.'''
    currentX = 0
    currentY = 0
    # always 0..3, north, east etc. clockwise
    direction = 0

    def rotate(self, left):
        '''Rotate current position by 90 degrees.
           left: boolean, indicating rotate direction (relative to
           current direction).'''

        if left:
            self.direction -= 1
            if self.direction < 0: self.direction = 3
        else:
            self.direction += 1
            if self.direction > 3: self.direction = 0

    def go_one_piece(self):
        # Calculate changeX, changeY
        changeX = changeY = 0
        if   self.direction == 0: changeY = 1
        elif self.direction == 1: changeX = 1
        elif self.direction == 2: changeY = -1
        elif self.direction == 3: changeX = -1

        self.currentX += changeX
        self.currentY += changeY

        self.make_line(self.currentX, self.currentY)

    def go(self, level, from_left):
        if level == 0: return

        self.rotate(not from_left)
        self.go(level - 1, not from_left)
        self.rotate(from_left)

        self.go_one_piece()

        self.go(level - 1, from_left)

        self.rotate(not from_left)
        self.go_one_piece()
        self.rotate(from_left)

        self.go(level - 1, from_left)

        self.rotate(not from_left)
        self.rotate(not from_left)
        self.go_one_piece()

        self.rotate(not from_left)
        self.go(level - 1, not from_left)
        self.rotate(not from_left)

    def make_line(self, x, y):
        '''Override this in subclasses to actually display hilbert curve.'''
        pass

class HilbertCurveBlender(HilbertCurve):
    '''Hilbert curve generator creating a Blender mesh.'''

    def make_line(self, x, y):
        self.vertices.append((x, y, 0))
        l = len(self.vertices)
        self.edges.append((l-2, l-1))

    def go_final(self, level, from_left):

        self.vertices = [(0, 0, 0)]
        self.edges = []

        self.go(level, from_left)

        mesh = bpy.data.meshes.new("HilbertCurve")
        mesh.from_pydata(self.vertices, self.edges, [])
        mesh.update()

        obj = bpy.data.objects.new("HilbertCurveObject", mesh)
        bpy.context.scene.objects.link(obj)

if __name__ == '__main__':
    h = HilbertCurveBlender()
    h.go_final(4, True)
    bpy.ops.wm.save_as_mainfile(filepath="/tmp/hilbert.blend")
