<commit_before>import time
import numpy as np


def iteratePairs(iterable):
    last = None
    for idx in range(len(iterable) - 1):
        nxt = iterable.next()
        if last is not None:
            yield last, nxt
        last = nxt


def formatFloat(fl, fmt=(4, 4)):
    stringFmt = "%.0" + str(fmt[1]) + "f"
    string = stringFmt % fl
    intPos, decPos = string.split(".")

    if len(intPos) > fmt[0]:
        print("Warning! position %f is getting truncated (msb)!")
        intPos = intPos[0:3]

    return intPos + decPos


def fmtCoord(xLoc, yLoc, xFmt=(4, 4), yFmt=(4, 4)):
    return "X" + formatFloat(xLoc, fmt=xFmt) + "Y" + formatFloat(yLoc, fmt=yFmt)


class GerberWriter:
    def __init__(self, pathOrFlo):
        if hasattr(pathOrFlo, "write"):
            self.f = pathOrFlo
        else:
            self.f = open(pathOrFlo, "w")

        # Flags to track the current file state
        self.headerWritten = False
        self.formatSet = False
        self.polaritySet = False
        self.aperatureCount = 0

    def setCoordFmt(self, xFmt=(4, 4), yFmt=(4, 4), units="MM"):
        assert units in ["MM", "IN"], "Units much be IN or MM"
        assert len(xFmt) == 2
        assert len(yFmt) == 2
        # MRG TODO: max extents and bounds of coord?
        self.xFmt = xFmt
        self.yFmt = yFmt
        self.units = units
        self.formatSet = True

    def writeComment(self, comment):
        assert "*" not in comment, "* character not allowed in comments"
        self.f.write("G04" + comment + "*\n")

    def writeGerberHeader(self, comment=None):
        if comment is None:
            comment = "Autogenerated by babyfood by MRG. " + time.ctime()
        self.writeComment(comment)

        # If the coordiante format hs not been set yet, put in the defaults
        if not self.formatSet:
            self.setCoordFmt()

        fmtStr = "X%i%iY%i%i" % (self.xFmt + self.yFmt)
        self.f.write("%FSLA" + fmtStr + "*%\n")
        self.f.write("%MO" + self.units + "*%\n")

        self.headerWritten = True

    def writeLayerPolarity(self, polarity="D"):
        self._forceHeader()
        assert polarity in ["D", "C"]
        self.f.write("%LP" + polarity + "*%\n")
        self.polaritySet = True

    def _forceHeader(self):
        if not self.headerWritten:
            self.writeGerberHeader()

    def _forcePolarity(self):
        if not self.polaritySet:
            self.writeLayerPolarity()

    def _checkAperature(self):
        if self.currentAperatureCode is None:
            raise RuntimeError("No Aperature defined for current move!")

    def _preGraphicsCheck(self):
        self._forceHeader()
        self._forcePolarity()
        self._checkAperature()

    def defineAperature(self, diameter, setAsCurrent=False):
        self._forceHeader()
        self._forcePolarity()

        # MRG TODO: Unique on aperature params
        # Raise warning when count becomes > 999 (legacy support)
        if self.aperatureCount > 999:
            print("WARNING! Aperature counts above 999 not supported by all machines.")

        thisAperatureCode = "D%i" % (10 + self.aperatureCount)
        aprDiaStr = "%0.03f" % diameter
        self.f.write("%AD" + thisAperatureCode + "C," + aprDiaStr + "*%\n")

        if setAsCurrent:
            self.setAperature(thisAperatureCode)

        self.aperatureCount += 1
        return thisAperatureCode

    def setAperature(self, aperatureCode):
        assert aperatureCode[0] == 'D'
        int(aperatureCode[1:])
        self.f.write(aperatureCode + "*\n")

    def finishGerber(self):
        self.f.write("M02*\n")
        self.f.close()

    def _linearMove(self, endX, endY, dCode):
        '''Low level command which encompasses all linear moves,
        This includes light, dark, and flash moves'''

        formattedCoords = fmtCoord(endX, endY, self.xFmt, self.yFmt)

        dCodeStr = "D%02i" % dCode
        # Stroke w/ current aperature
        if dCode == 1:
            cmdString = "G01" + formattedCoords + dCodeStr
        # Light off move and flash
        elif dCode in [2, 3]:
            cmdString = formattedCoords + dCodeStr
        else:
            raise RuntimeError("Unrecognized dCode " + dCodeStr)
        self.f.write(cmdString + "*\n")

    def move(self, newX, newY):
        self._linearMove(newX, newY, 2)

    def lineTo(self, newX, newY):
        self._linearMove(newX, newY, 1)

    def flashAt(self, newX, newY):
        self._linearMove(newX, newY, 3)

    def simplePolygon(self, xs, ys):
        # Start "polygon mode" I think this ignores aperatures
        self.f.write("G36*\n")

        for n, (xC, yC) in enumerate(zip(xs, ys)):
            if n == 0:
                # Move to the start point
                self._linearMove(xC, yC, 2)
            else:
                # Trace the next segment
                self._linearMove(xC, yC, 1)
        # If the start and end points are not equal, close the loop
        if (xs[0] != xs[-1]) or (ys[0] != ys[-1]):
            self._linearMove(xs[0], ys[0], 1)

        # Finish the contour
        self.f.write("G37*\n")


if __name__ == "__main__":
    # Quick test of building functionality
    gw = GerberWriter("gwTest.gbo")
    gw.defineAperature(0.25, True)
    gw.move(0, 0)
    gw.lineTo(10, 0)
    gw.lineTo(10, 10)
    gw.lineTo(0, 10)
    gw.lineTo(0, 0)

    gw.flashAt(4, 2)
    gw.flashAt(2, 2)
    gw.flashAt(2, 4)
    gw.flashAt(4, 4)

    for n, r in enumerate(np.linspace(0, 5, 11)[::-1]):
        t = np.linspace(0, 2 * np.pi, 7) + r * 0.3
        xs = r * np.cos(t) + 5
        ys = r * np.sin(t) + 5
        gw.writeLayerPolarity(["D", "C"][n % 2])
        gw.simplePolygon(xs, ys)

    gw.finishGerber()<commit_msg>More inprovements.  More consistant naming.<commit_after>import time
import numpy as np


def iteratePairs(iterable):
    last = None
    for idx in range(len(iterable) - 1):
        nxt = iterable.next()
        if last is not None:
            yield last, nxt
        last = nxt


def formatFloat(fl, fmt=(4, 4)):
    stringFmt = "%.0" + str(fmt[1]) + "f"
    string = stringFmt % fl
    intPos, decPos = string.split(".")

    if len(intPos) > fmt[0]:
        print("Warning! position %f is getting truncated (msb)!")
        intPos = intPos[0:3]

    return intPos + decPos


def fmtCoord(xLoc, yLoc, xFmt=(4, 4), yFmt=(4, 4)):
    return "X" + formatFloat(xLoc, fmt=xFmt) + "Y" + formatFloat(yLoc, fmt=yFmt)


class GerberWriter:
    def __init__(self, pathOrFlo):
        if hasattr(pathOrFlo, "write"):
            self.f = pathOrFlo
        else:
            self.f = open(pathOrFlo, "w")

        # Flags to track the current file state
        self.headerWritten = False
        self.formatSet = False
        self.polaritySet = False
        self.aperatureCount = 0

    def setCoordFmt(self, xFmt=(4, 4), yFmt=(4, 4), units="MM"):
        assert units in ["MM", "IN"], "Units much be IN or MM"
        assert len(xFmt) == 2
        assert len(yFmt) == 2
        # MRG TODO: max extents and bounds of coord?
        self.xFmt = xFmt
        self.yFmt = yFmt
        self.units = units
        self.formatSet = True

    def writeComment(self, comment):
        assert "*" not in comment, "* character not allowed in comments"
        self.f.write("G04" + comment + "*\n")

    def writeGerberHeader(self, comment=None):
        if comment is None:
            comment = "Autogenerated by babyfood by MRG. " + time.ctime()
        self.writeComment(comment)

        # If the coordiante format hs not been set yet, put in the defaults
        if not self.formatSet:
            self.setCoordFmt()

        fmtStr = "X%i%iY%i%i" % (self.xFmt + self.yFmt)
        self.f.write("%FSLA" + fmtStr + "*%\n")
        self.f.write("%MO" + self.units + "*%\n")

        self.headerWritten = True

    def writeLayerPolarity(self, polarity="D"):
        self._forceHeader()
        assert polarity in ["D", "C"]
        self.f.write("%LP" + polarity + "*%\n")
        self.polaritySet = True

    def _forceHeader(self):
        if not self.headerWritten:
            self.writeGerberHeader()

    def _forcePolarity(self):
        if not self.polaritySet:
            self.writeLayerPolarity()

    def _checkAperature(self):
        if self.currentAperatureCode is None:
            raise RuntimeError("No Aperature defined for current move!")

    def _preGraphicsCheck(self):
        self._forceHeader()
        self._forcePolarity()

    def defineAperature(self, diameter, setAsCurrent=False):
        self._forceHeader()
        self._forcePolarity()

        # MRG TODO: Unique on aperature params
        # Raise warning when count becomes > 999 (legacy support)
        if self.aperatureCount > 999:
            print("WARNING! Aperature counts above 999 not supported by all machines.")

        thisAperatureCode = "D%i" % (10 + self.aperatureCount)
        aprDiaStr = "%0.03f" % diameter
        self.f.write("%AD" + thisAperatureCode + "C," + aprDiaStr + "*%\n")

        if setAsCurrent:
            self.setAperature(thisAperatureCode)

        self.aperatureCount += 1
        return thisAperatureCode

    def setAperature(self, aperatureCode):
        assert aperatureCode[0] == 'D'
        int(aperatureCode[1:])
        self.f.write(aperatureCode + "*\n")

    def finalize(self):
        self.f.write("M02*\n")
        self.f.close()

    def _linearMove(self, endX, endY, dCode):
        '''Low level command which encompasses all linear moves,
        This includes light, dark, and flash moves'''
        self._preGraphicsCheck()
        print("LM:", endX, endY, dCode)

        formattedCoords = fmtCoord(endX, endY, self.xFmt, self.yFmt)
        dCodeStr = "D%02i" % dCode
        # Stroke w/ current aperature
        if dCode == 1:
            cmdString = "G01" + formattedCoords + dCodeStr
        # Light off move and flash
        elif dCode in [2, 3]:
            cmdString = formattedCoords + dCodeStr
        else:
            raise RuntimeError("Unrecognized dCode " + dCodeStr)
        self.f.write(cmdString + "*\n")

    def move(self, newX, newY):
        self._linearMove(newX, newY, 2)

    def lineTo(self, newX, newY):
        self._linearMove(newX, newY, 1)

    def flashAt(self, newX, newY):
        self._linearMove(newX, newY, 3)

    def simplePolygon(self, xs, ys):
        # Start "polygon mode" I think this ignores aperatures
        self.f.write("G36*\n")
        print(xs, xs.shape)
        for n, (xC, yC) in enumerate(zip(xs, ys)):
            print("SP:", n, xC, yC)
            if n == 0:
                # Move to the start point
                self._linearMove(xC, yC, 2)
            else:
                # Trace the next segment
                self._linearMove(xC, yC, 1)
        # If the start and end points are not equal, close the loop
        if (xs[0] != xs[-1]) or (ys[0] != ys[-1]):
            self._linearMove(xs[0], ys[0], 1)

        # Finish the contour
        self.f.write("G37*\n")


if __name__ == "__main__":
    # Quick test of building functionality
    gw = GerberWriter("gwTest.gbo")
    gw.defineAperature(0.25, True)
    gw.move(0, 0)
    gw.lineTo(10, 0)
    gw.lineTo(10, 10)
    gw.lineTo(0, 10)
    gw.lineTo(0, 0)

    gw.flashAt(4, 2)
    gw.flashAt(2, 2)
    gw.flashAt(2, 4)
    gw.flashAt(4, 4)

    for n, r in enumerate(np.linspace(0, 5, 11)[::-1]):
        t = np.linspace(0, 2 * np.pi, 7) + r * 0.3
        xs = r * np.cos(t) + 5
        ys = r * np.sin(t) + 5
        gw.writeLayerPolarity(["D", "C"][n % 2])
        gw.simplePolygon(xs, ys)

    gw.finalize()
{'fraction_non_alphanumeric': 0.12702516373664255, 'fraction_numerical': 0.017580144777662874, 'mean_word_length': 2.150963888134673, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6837130', 'n_tokens_mistral': 4129, 'n_tokens_neox': 3854, 'n_words': 1031}