示例#1
0
def _(*reps: RowRepeat) -> Knittable:
    if not all(
            map(lambda item: len(set(map(type, item))) == 1,
                _padded_zip(*map(attrgetter("rows"), reps)))):
        # Unroll all row repeats if we see a row and a row repeat side-by-side.
        # This is conservative, but repetitive output can be fixed up by
        # _roll_repeated_rows.
        #
        # noinspection PyTypeChecker
        rows = list(
            starmap(
                _merge_across,
                _padded_zip(*map(attrgetter("rows"),
                                 map(partial(_flatten, unroll=True), reps)))))
        return RowRepeat(rows=rows,
                         times=NaturalLit.of(1),
                         consumes=rows[0].consumes,
                         produces=rows[-1].produces,
                         sources=list(_flat_map(attrgetter("sources"), reps)))

    # Find the smallest number of rows that all row repeats can be expanded to.
    num_rows = _lcm(*map(lambda rep: sum(map(count_rows, rep.rows)), reps))

    def expand(rep: RowRepeat) -> Iterator[Node]:
        times = min(rep.times.value,
                    num_rows // sum(map(count_rows, rep.rows)))
        return _repeat_rows(rep.rows, times)

    rows = list(starmap(_merge_across, _padded_zip(*map(expand, reps))))
    return RowRepeat(rows=rows,
                     times=NaturalLit.of(
                         ceil(max(map(count_rows, reps)) / num_rows)),
                     consumes=rows[0].consumes,
                     produces=rows[-1].produces,
                     sources=list(_flat_map(attrgetter("sources"), reps)))
示例#2
0
def _(rep: FixedStitchRepeat, unroll: bool = False) -> Node:
    # Cases where the only node a fixed stitch repeat contains is another fixed
    # stitch repeat should be flattened by multiplying the repeat times
    # together.
    if (rep.times.value != 1 and len(rep.stitches) == 1
            and isinstance(rep.stitches[0], FixedStitchRepeat)):
        first = rep.stitches[0]
        assert isinstance(first, FixedStitchRepeat)
        # noinspection PyTypeChecker
        return ast_map(
            replace(rep,
                    stitches=first.stitches,
                    times=NaturalLit.of(first.times.value * rep.times.value),
                    consumes=first.consumes * rep.times.value,
                    produces=first.produces * rep.times.value),
            partial(_flatten, unroll=unroll))
    else:
        stitches = []
        # noinspection PyTypeChecker
        for stitch in map(partial(_flatten, unroll=unroll), rep.stitches):
            if (isinstance(stitch, FixedStitchRepeat)
                    and stitch.times.value == 1):
                # Un-nest fixed stitch repeats that only repeat once.
                stitches.extend(stitch.stitches)
            else:
                stitches.append(stitch)
        return replace(rep, stitches=stitches)
示例#3
0
def _(expanding: KnitScriptParser.ExpandingStitchRepeatContext) -> Node:
    return ExpandingStitchRepeat(
        stitches=list(map(build_ast, _get_stitches(expanding))),
        to_last=(build_ast(expanding.toLast)
                 if expanding.toLast else NaturalLit.of(0)),
        consumes=None,
        produces=None,
        sources=[_get_source(expanding)])
示例#4
0
def _normalize_row_repeat(rep: RowRepeat) -> RowRepeat:
    # Make sure every row repeat has an even number of rows inside of it, so
    # the knitter doesn't have to reverse rows in their head every other
    # iteration.
    if not (rep.times.value > 1 and _has_explicit_sides(rep)
            and sum(map(count_rows, rep.rows)) % 2 != 0):
        return rep
    twice = replace(rep,
                    rows=list(_repeat_rows(rep.rows, 2)),
                    times=NaturalLit.of(rep.times.value // 2))
    if rep.times.value % 2 == 0:
        return twice
    else:
        return RowRepeat(rows=[twice, *rep.rows],
                         times=NaturalLit.of(1),
                         consumes=rep.consumes,
                         produces=rep.produces,
                         sources=rep.sources)
示例#5
0
def _(expanding: ExpandingStitchRepeat, n: int) -> Node:
    # noinspection PyTypeChecker
    return replace(
        expanding,
        stitches=list(
            map(partial(_increase_expanding_repeats, n=n),
                expanding.stitches)),
        to_last=NaturalLit.of(expanding.to_last.value + n),
    )
示例#6
0
def _(row: Row, times: int) -> Node:
    return replace(row,
                   stitches=[
                       FixedStitchRepeat(stitches=row.stitches,
                                         times=NaturalLit.of(times),
                                         consumes=row.consumes * times,
                                         produces=row.produces * times,
                                         sources=row.sources)
                   ],
                   consumes=row.consumes * times,
                   produces=row.produces * times)
示例#7
0
def fill(pattern: Pattern, width: int, height: int) -> Node:
    """
    Repeats a pattern horizontally and vertically to fill a box.

    :param pattern: the pattern to repeat
    :param width: the width of the box (number of stitches)
    :param height: the height of the box (number of rows)
    :return: the repeated pattern
    """
    pattern = infer_counts(pattern)
    assert isinstance(pattern, Pattern)
    stitches = max(map(attrgetter("consumes"), pattern.rows))
    rows = count_rows(pattern)
    if (width % stitches != 0 or width < stitches or height % rows != 0
            or height < rows):
        raise InterpretError(
            f"{stitches}×{rows} pattern does not fit evenly into " +
            f"{width}×{height} fill box", pattern)
    n = width // stitches
    m = height // rows
    return replace(pattern,
                   rows=[
                       RowRepeat(rows=[
                           FixedBlockRepeat(block=Block(
                               patterns=[pattern],
                               consumes=pattern.consumes,
                               produces=pattern.produces,
                               sources=pattern.sources),
                                            times=NaturalLit.of(n),
                                            consumes=pattern.consumes * n,
                                            produces=pattern.produces * n,
                                            sources=pattern.sources)
                       ],
                                 times=NaturalLit.of(m),
                                 consumes=pattern.consumes * n,
                                 produces=pattern.produces * n,
                                 sources=pattern.sources)
                   ],
                   consumes=pattern.consumes * n,
                   produces=pattern.produces * n)
示例#8
0
def _(rep: RowRepeat, unroll: bool = False) -> Node:
    flattened_rows = []
    # noinspection PyTypeChecker
    for row in map(partial(_flatten, unroll=unroll), rep.rows):
        if isinstance(row, RowRepeat) and (unroll or row.times.value <= 1):
            flattened_rows.extend(_repeat_rows(row.rows, row.times.value))
        elif isinstance(row, Pattern):
            flattened_rows.extend(row.rows)
        else:
            flattened_rows.append(row)

    if (rep.times.value > 1 and len(flattened_rows) == 1
            and isinstance(flattened_rows[0], RowRepeat)):
        # Cases where the only node a row repeat contains is another row
        # repeat should be flattened by multiplying the repeat times together.
        return _normalize_row_repeat(
            replace(rep,
                    rows=flattened_rows[0].rows,
                    times=NaturalLit.of(rep.times.value *
                                        flattened_rows[0].times.value)))
    return _normalize_row_repeat(replace(rep, rows=flattened_rows))
示例#9
0
 def roll(rows):
     if not rows:
         return []
     for size in range(1, len(rows) // 2 + 1):
         sections = _chunks(rows, size)
         first = next(sections)
         # noinspection PyTypeChecker
         times = len(
             list(takewhile(partial(_eq_ignore_sides, first), sections)))
         if times > 0:
             rolled = RowRepeat(rows=first,
                                times=NaturalLit.of(times + 1),
                                consumes=first[0].consumes,
                                produces=first[-1].produces,
                                sources=list(
                                    _flat_map(attrgetter("sources"),
                                              first)))
             # Only roll up if the total number of rows is greater than some
             # threshold to avoid creating lots of little row repeats.
             if count_rows(rolled) >= 4:
                 return [rolled, *roll(rows[size * (times + 1):])]
     return [rows[0], *roll(rows[1:])]
示例#10
0
    def combine(acc, node):
        try:
            current_stitch, current_times = get_stitch(node)
            last_stitch, last_times = get_stitch(acc[-1])
        except (IndexError, TypeError, ValueError):
            return acc + [node]
        if current_stitch != last_stitch:
            return acc + [node]

        times = current_times + last_times
        return acc[:-1] + [
            FixedStitchRepeat(stitches=[
                StitchLit(value=current_stitch,
                          consumes=current_stitch.consumes,
                          produces=current_stitch.produces,
                          sources=acc[-1].sources + node.sources)
            ],
                              times=NaturalLit.of(times),
                              consumes=current_stitch.consumes * times,
                              produces=current_stitch.produces * times,
                              sources=acc[-1].sources + node.sources)
        ]
示例#11
0
def _(pattern: Pattern, times: int = 1) -> Node:
    return RowRepeat(rows=pattern.rows,
                     times=NaturalLit.of(times),
                     consumes=pattern.consumes,
                     produces=pattern.produces,
                     sources=pattern.sources)
示例#12
0
def _(rep: ExpandingStitchRepeat) -> Node:
    return FixedStitchRepeat(stitches=rep.stitches,
                             times=NaturalLit.of(1),
                             consumes=None,
                             produces=None,
                             sources=rep.sources)
示例#13
0
def _(row: Row) -> Node:
    return FixedStitchRepeat(stitches=row.stitches,
                             times=NaturalLit.of(1),
                             consumes=row.consumes,
                             produces=row.produces,
                             sources=row.sources)
示例#14
0
def _(rep: ExpandingStitchRepeat, before: int) -> Node:
    fixed = _reverse(to_fixed_repeat(rep), before)
    assert isinstance(fixed, FixedStitchRepeat)
    return replace(rep, stitches=fixed.stitches, to_last=NaturalLit.of(before))
示例#15
0
def _(natural: KnitScriptParser.NaturalContext) -> Node:
    return NaturalLit(value=int(natural.getText()),
                      sources=[_get_source(natural)])