def transform(self, transforms):
        """Transforms start, layers using the given transforms."""
        layers = self.layers
        start = self.start

        # loop through all single-layer transformations to all layers
        for i, layer in enumerate(layers):
            a = layer.rows  # aka the memory bucket
            b = layer.rows  # aka the current bucket

            logmsg('transform', 'Transformation buckets before layer %d:' % i)
            loglines('transform', lambda: self.str_buckets(a, b))

            left = transforms
            for t in transforms:
                param, cmd = t
                left = left[1:]  # remove this cmd from the remaining cmds

                if cmd == 'halign':
                    self.halign = param
                elif cmd == 'valign':
                    self.valign = param
                elif cmd == '!':
                    # The ! command just updates A to match B
                    a = b
                else:
                    a, b = self.apply_transform(t, a, b)  # do the transform

                    # adjust start pos for 'n' and 'w' commands
                    if cmd == 'n':
                        start = add_points(start, (0, layers[0].height() *
                                                   (param - 1)))
                    elif cmd == 'w':
                        start = add_points(start, (layers[0].width() *
                                                   (param - 1), 0))

                if cmd in ('halign', 'valign'):
                    logmsg('transform', 'Set %s=%s' % (t[1], t[0]))
                else:
                    logmsg('transform', 'Buckets after command %s%s:' % t)
                    loglines('transform', lambda: self.str_buckets(a, b))

                # we'll return the result in b
                layers[i].rows = b

        self.start, self.layers = start, layers
        return
    def transform(self, transforms):
        """Transforms start, layers using the given transforms."""
        layers = self.layers
        start = self.start

        # loop through all single-layer transformations to all layers
        for i, layer in enumerate(layers):
            a = layer.rows  # aka the memory bucket
            b = layer.rows  # aka the current bucket

            logmsg('transform', 'Transformation buckets before layer %d:' % i)
            loglines('transform', lambda: self.str_buckets(a, b))

            left = transforms
            for t in transforms:
                param, cmd = t
                left = left[1:]  # remove this cmd from the remaining cmds

                if cmd == 'halign':
                    self.halign = param
                elif cmd == 'valign':
                    self.valign = param
                elif cmd == '!':
                    # The ! command just updates A to match B
                    a = b
                else:
                    a, b = self.apply_transform(t, a, b)  # do the transform

                    # adjust start pos for 'n' and 'w' commands
                    if cmd == 'n':
                        start = add_points(start, (0, layers[0].height() * (param - 1)))
                    elif cmd == 'w':
                        start = add_points(start, (layers[0].width() * (param - 1), 0))

                if cmd in ('halign', 'valign'):
                    logmsg('transform', 'Set %s=%s' % (t[1], t[0]))
                else:
                    logmsg('transform', 'Buckets after command %s%s:' % t)
                    loglines('transform', lambda: self.str_buckets(a, b))

                # we'll return the result in b
                layers[i].rows = b

        self.start, self.layers = start, layers
        return
    def get_info(self):
        """Retrieve various bits of info about the blueprint."""
        rowsets = [layer.grid.rows for layer in self.layers]
        cells = map(lambda r: r.flatten(), rowsets)
        if len(cells) == 0:
            raise BlueprintError('No row data in blueprint.')
        commands = [c.command for c in cells[0]]
        cmdset = set(commands)  # uniques
        if '' in cmdset:
            cmdset.remove('')

        # count the number of occurrences of each command in the blueprint
        counts = [(c, commands.count(c)) for c in cmdset]
        counts.sort(key=lambda x: x[1], reverse=True)

        # look for the manual-mat character anywhere in the commands,
        # and check that phase=build (the only mode that we'll support
        # manual material selection with right now)
        uses_manual_mats = util.is_substring_in_list(':', cmdset) and \
            self.build_type == 'build'

        # make a row of repeating numbers to annotate the blueprint with
        width = self.layers[0].grid.width

        # build the blueprint preview
        return textwrap.dedent("""
            Blueprint name: %s
            Build type: %s
            Comment: %s
            Start position: %s
            Start comment: %s
            First layer width: %d
            First layer height: %d
            Layer count: %d
            Uses manual material selection: %s
            Command use counts: %s
            """).strip() % (
                self.name,
                self.build_type,
                self.comment or '',
                add_points(self.start, (1, 1)),
                self.start_comment or '',
                width,
                self.layers[0].grid.height,
                len(self.layers),
                uses_manual_mats,
                ', '.join("%s:%d" % c for c in counts)
                ) + \
            "\nBlueprint preview:\n" + \
                '\n'.join(
                    Grid.str_commands(layer.grid.rows, annotate=True) + \
                        '\n#' + ''.join(layer.onexit)
                    for layer in self.layers
                )
Exemple #4
0
    def get_info(self):
        """Retrieve various bits of info about the blueprint."""
        rowsets = [layer.grid.rows for layer in self.layers]
        cells = map(lambda r: r.flatten(), rowsets)
        if len(cells) == 0:
            raise BlueprintError('No row data in blueprint.')
        commands = [c.command for c in cells[0]]
        cmdset = set(commands)  # uniques
        if '' in cmdset:
            cmdset.remove('')

        # count the number of occurrences of each command in the blueprint
        counts = [(c, commands.count(c)) for c in cmdset]
        counts.sort(key=lambda x: x[1], reverse=True)

        # look for the manual-mat character anywhere in the commands,
        # and check that phase=build (the only mode that we'll support
        # manual material selection with right now)
        uses_manual_mats = util.is_substring_in_list(':', cmdset) and \
            self.build_type == 'build'

        # make a row of repeating numbers to annotate the blueprint with
        width = self.layers[0].grid.width

        # build the blueprint preview
        return textwrap.dedent("""
            Blueprint name: %s
            Build type: %s
            Comment: %s
            Start position: %s
            Start comment: %s
            First layer width: %d
            First layer height: %d
            Layer count: %d
            Uses manual material selection: %s
            Command use counts: %s
            """).strip() % (
                self.name,
                self.build_type,
                self.comment or '',
                add_points(self.start, (1, 1)),
                self.start_comment or '',
                width,
                self.layers[0].grid.height,
                len(self.layers),
                uses_manual_mats,
                ', '.join("%s:%d" % c for c in counts)
                ) + \
            "\nBlueprint preview:\n" + \
                '\n'.join(
                    Grid.str_commands(layer.grid.rows, annotate=True) + \
                        '\n#' + ''.join(layer.onexit)
                    for layer in self.layers
                )
def get_nearest_plottable_area_from(grid, start):
    """
    Find the nearest plottable area corner from start.
    Returns coordinate of nearest plottable area corner.
    """

    # if start is out of bounds for grid, expand dimensions of grid
    if grid.is_out_of_bounds(*start):
        grid.expand_dimensions(start[0] + 1, start[1] + 1)

    # check the cell we started in: if it is plottable, it becomes our
    # starting cheapest_area
    cell = grid.get_cell(*start)

    if cell.plottable and cell.area:
        return start

    # start with the innermost ring of cells adjacent to start, then
    # expand outward ring by ring
    for ring in xrange(1, 1 + max([grid.width, grid.height])):
        # starting position in this ring (=NW corner cell of ring)
        pos = add_points(start, (-ring, -ring))

        for direction in (Direction(d) for d in ['e', 's', 'w', 'n']):
            for _ in xrange(0, 2 * ring):
                pos = add_points(pos,
                                 direction.delta())  # step once in direction

                if grid.is_out_of_bounds(*pos):
                    continue  # outside grid bounds

                corner = grid.get_cell(*pos)

                if corner.plottable and corner.area:
                    # cell has an area that can be plotted, return it
                    return pos

    # found no position with an area we can plot
    return None
Exemple #6
0
def get_nearest_plottable_area_from(grid, start):
    """
    Find the nearest plottable area corner from start.
    Returns coordinate of nearest plottable area corner.
    """

    # if start is out of bounds for grid, expand dimensions of grid
    if grid.is_out_of_bounds(*start):
        grid.expand_dimensions(start[0] + 1, start[1] + 1)

    # check the cell we started in: if it is plottable, it becomes our
    # starting cheapest_area
    cell = grid.get_cell(*start)

    if cell.plottable and cell.area:
        return start

    # start with the innermost ring of cells adjacent to start, then
    # expand outward ring by ring
    for ring in xrange(1, 1 + max([grid.width, grid.height])):
        # starting position in this ring (=NW corner cell of ring)
        pos = add_points(start, (-ring, -ring))

        for direction in (Direction(d) for d in ['e', 's', 'w', 'n']):
            for _ in xrange(0, 2 * ring):
                pos = add_points(pos, direction.delta())  # step once in direction

                if grid.is_out_of_bounds(*pos):
                    continue  # outside grid bounds

                corner = grid.get_cell(*pos)

                if corner.plottable and corner.area:
                    # cell has an area that can be plotted, return it
                    return pos

    # found no position with an area we can plot
    return None
            if dx == 0:
                steps = dy  # moving on y axis only
            elif dy == 0:
                steps = dx  # moving on x axis only
            else:
                # determine max diagonal steps we can take
                # in this direction without going too far
                steps = min([dx, dy])

            keycode = ['[' + direction.compass + ']']
            jumpkeycode = ['[+' + direction.compass + ']']
            move = direction.delta()
            if not allowjumps or steps < 8 or not allow_overshoot:
                # render single movement keys
                keys.extend(keycode * steps)
                (x1, y1) = add_points((x1, y1), scale_point(move, steps))
                allow_overshoot = True
            else:
                # use DF's move-by-10-units commands
                jumps = (steps // 10)
                leftover = steps % 10
                jumpmove = scale_point(move, 10)

                # backtracking optimization
                if leftover >= 8:
                    # test if jumping an extra 10-unit step
                    # would put us outside of the bounds of
                    # the blueprint (want to prevent)
                    (xt, yt) = add_points((x1, y1), scale_point(jumpmove, (jumps + 1)))

                    if self.grid.is_out_of_bounds(xt, yt):
    def find_largest_area_in_quad(self, x, y, dir1, dir2, bestarea):
        """
        Given the quad starting at (x, y) and formed by dir1 and dir2
        (treated as rays with (x, y) as origin), we find the max
        contiguous-cell distance along dir1, then for each position
        along dir1, we find the max contiguous-cell distance along
        dir2. This allows us to find the largest contiguous area
        constructable by travelling down dir1, then at a right angle
        along dir2 for each position.

        Returns the largest area found.
        """

        command = self.grid.get_cell(x, y).command

        # Get the min/max size that this area may be, based on the command
        sizebounds = self.buildconfig.get('sizebounds', command) \
            or (1, 1000, 1, 1000)  # default sizebounds are very large

        # Get the max width of this area on the axis defined by
        # pos and dir1 direction, and max width from
        # the dir2.
        # width and height are conceptually aligned to an
        # east(dir1) x south(dir2) quad below.
        maxwidth = self.grid.count_contiguous_cells(x, y, dir1)
        maxheight = self.grid.count_contiguous_cells(x, y, dir2)

        if maxwidth < sizebounds[0]:
            # constructions narrower than the minimum width for this
            # command are ineligible to be any larger than 1 wide
            maxwidth = 1
        elif maxwidth > sizebounds[1]:
            # don't let constructions be wider than allowed
            maxwidth = sizebounds[1]

        if maxheight < sizebounds[2]:
            # constructions shorter than the minimum height for this
            # command are ineligible to be any larger than 1 tall
            maxheight = 1
        elif maxheight > sizebounds[3]:
            # don't let constructions be taller than allowed
            maxheight = sizebounds[3]

        if maxwidth * maxheight < bestarea.size():
            return None  # couldn't be larger than the best one yet found

        if maxheight == 1 and maxwidth == 1:
            # 1x1 area, just return it
            return Area((x, y), (x, y))

        # (width x 1) sized area
        bestarea = Area((x, y),
                        add_points((x, y),
                                   scale_point(dir1.delta(), maxwidth - 1)))

        for ydelta in range(1, maxheight):
            (xt, yt) = add_points((x, y), scale_point(dir2.delta(), ydelta))

            height = ydelta + 1
            width = self.grid.count_contiguous_cells(xt, yt, dir1)

            if width > maxwidth:
                # this row can't be wider than previous rows
                width = maxwidth
            elif width < maxwidth:
                # successive rows can't be wider than this row
                maxwidth = width

            if width * height > bestarea.size():
                bestarea = Area((x, y),
                                add_points((xt, yt),
                                           scale_point(dir1.delta(),
                                                       width - 1)))
            else:
                continue

        return bestarea
            if dx == 0:
                steps = dy  # moving on y axis only
            elif dy == 0:
                steps = dx  # moving on x axis only
            else:
                # determine max diagonal steps we can take
                # in this direction without going too far
                steps = min([dx, dy])

            keycode = ['[' + direction.compass + ']']
            jumpkeycode = ['[+' + direction.compass + ']']
            move = direction.delta()
            if not allowjumps or steps < 8 or not allow_overshoot:
                # render single movement keys
                keys.extend(keycode * steps)
                (x1, y1) = add_points((x1, y1), scale_point(move, steps))
                allow_overshoot = True
            else:
                # use DF's move-by-10-units commands
                jumps = (steps // 10)
                leftover = steps % 10
                jumpmove = scale_point(move, 10)

                # backtracking optimization
                if leftover >= 8:
                    # test if jumping an extra 10-unit step
                    # would put us outside of the bounds of
                    # the blueprint (want to prevent)
                    (xt, yt) = add_points((x1, y1),
                                          scale_point(jumpmove, (jumps + 1)))
    def find_largest_area_in_quad(self, x, y, dir1, dir2, bestarea):
        """
        Given the quad starting at (x, y) and formed by dir1 and dir2
        (treated as rays with (x, y) as origin), we find the max
        contiguous-cell distance along dir1, then for each position
        along dir1, we find the max contiguous-cell distance along
        dir2. This allows us to find the largest contiguous area
        constructable by travelling down dir1, then at a right angle
        along dir2 for each position.

        Returns the largest area found.
        """

        command = self.grid.get_cell(x, y).command

        # Get the min/max size that this area may be, based on the command
        sizebounds = self.buildconfig.get('sizebounds', command) \
            or (1, 1000, 1, 1000)  # default sizebounds are very large

        # Get the max width of this area on the axis defined by
        # pos and dir1 direction, and max width from
        # the dir2.
        # width and height are conceptually aligned to an
        # east(dir1) x south(dir2) quad below.
        maxwidth = self.grid.count_contiguous_cells(x, y, dir1)
        maxheight = self.grid.count_contiguous_cells(x, y, dir2)

        if maxwidth < sizebounds[0]:
            # constructions narrower than the minimum width for this
            # command are ineligible to be any larger than 1 wide
            maxwidth = 1
        elif maxwidth > sizebounds[1]:
            # don't let constructions be wider than allowed
            maxwidth = sizebounds[1]

        if maxheight < sizebounds[2]:
            # constructions shorter than the minimum height for this
            # command are ineligible to be any larger than 1 tall
            maxheight = 1
        elif maxheight > sizebounds[3]:
            # don't let constructions be taller than allowed
            maxheight = sizebounds[3]

        if maxwidth * maxheight < bestarea.size():
            return None  # couldn't be larger than the best one yet found

        if maxheight == 1 and maxwidth == 1:
            # 1x1 area, just return it
            return Area((x, y), (x, y))

        # (width x 1) sized area
        bestarea = Area((x, y),
            add_points(
                (x, y),
                scale_point(dir1.delta(), maxwidth - 1)
            )
        )

        for ydelta in range(1, maxheight):
            (xt, yt) = add_points(
                (x, y),
                scale_point(dir2.delta(), ydelta)
            )

            height = ydelta + 1
            width = self.grid.count_contiguous_cells(xt, yt, dir1)

            if width > maxwidth:
                # this row can't be wider than previous rows
                width = maxwidth
            elif width < maxwidth:
                # successive rows can't be wider than this row
                maxwidth = width

            if width * height > bestarea.size():
                bestarea = Area((x, y),
                    add_points(
                        (xt, yt),
                        scale_point(dir1.delta(), width - 1)
                    )
                )
            else:
                continue

        return bestarea