Example #1
0
    def produce_impl(self):
        # Creates the patterns

        # Convert parameters from [um] to [dbu] or database units

        d = self.specDiameter[self.diameter] / self.layout.dbu * 1000
        pFlat = self.specPrimaryFlat[self.diameter] / self.layout.dbu * 1000
        sFlat = self.specSecondaryFlat[self.diameter] / self.layout.dbu * 1000
        aFlat = self.specSecondaryFlatAngle[self.secondaryFlatAngle]
        b = self.border / self.layout.dbu * 1000
        dIn = self.dIn / self.layout.dbu * 1000
        dOut = self.dOut / self.layout.dbu * 1000

        # Create the wafer shape
        s = shape()
        region = s.siWafer(d, pFlat, sFlat, aFlat, 128)

        if (self.doubleFlat):
            regionTmp = region.dup()
            regionTmp.transform(pya.ICplxTrans(1, 180, False, 0, 0))
            region = region & regionTmp

        if (self.ring):
            scaleIn = (d - dIn) / d
            scaleOut = (d + dOut) / d
            regionOut = region.dup()
            regionOut.transform(pya.ICplxTrans(scaleOut, 0, False, 0, 0))
            regionIn = region.dup()
            regionIn.transform(pya.ICplxTrans(scaleIn, 0, False, 0, 0))
            region = regionOut - regionIn

        if (self.invert):
            region = s.invert(region, b)

        self.cell.shapes(self.layer_layer).insert(region)
Example #2
0
 def vernier(self, width, length, pitch):
   '''
   vernier(width, length, pitch)
   
   Generates a vernier scale
   
   Parameters
   ---------
   width : integer
         The width of each tick marker
   length : integer
         The length of the central tick mark
         The major tick marks are 3/4 this length
         The minor tick marks are half this length
   pitch : integer
         The distance between each tick mark
   
   Returns
   ------
   region : [pya.Region]
        A region containing the vernier scale
   
   Description
   ---------
   A pair of vernier scale can be used to measure misalignment by eye.
   
   In photolithography the wafer will contain one vernier pattern (width = 4, length = 40, pitch = 8, units = micron)
   and the mask will contain a second vernier pattern (pitch = 8.2) providing an alignment measurement resolution of 0.2 micron.
   '''
   scaleM = 0.75
   scaleS = 0.5
   
   # Create the large tick mark
   polygons = []
   #tick = pya.Polygon([pya.Point(0,0), pya.Point(length,0), pya.Point(length,width), pya.Point(0,width)])
   tick = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length,width), pya.Point(length,0)])
   tc = pya.ICplxTrans(int(-length/2),int(-width/2))
   polygons.append(tc.trans(tick))
   
   # Create the medium tick mark
   #tickm = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleM,0), pya.Point(length*scaleM,width), pya.Point(0,width)])
   tickm = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleM,width), pya.Point(length*scaleM,0)])
   pos = [-2, -1, 1, 2]
   for i in pos:
     tt = pya.ICplxTrans(0,int(i*pitch*5))
     polygons.append(tc.trans(tt.trans(tickm)))
   
   # Create the small tick mark
   #ticks = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleS,0), pya.Point(length*scaleS,width), pya.Point(0,width)])
   ticks = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleS,width), pya.Point(length*scaleS,0)])
   pos = [-9, -8, -7, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6, 7, 8, 9]
   for i in pos:
     tt = pya.ICplxTrans(0,int(i*pitch))
     polygons.append(tc.trans(tt.trans(ticks)))
     
   return pya.Region(polygons)
Example #3
0
    def pieceHolderCassette(self, dbu=1):
        '''
      pieceHolderCassette()
      
      Generates the shape of the Jeol JBX-5500FS piece holder cassette
      
      Parameters
      ---------
      dbu : double
            The database unit
            
      Returns
      ------
      region : [pya.Region]
           A region containing the piece holder cassette shape
           
      Description
      ------
      The center of this piece holder shape (0,0) is at stage position (62.5mm, 37.5mm)
      
      Jeol Stage Y axis is reverse of KLayout Y axis
      '''
        # Create the quarter circle
        r = 36100 / dbu
        rx = 62500 / dbu
        ry = -37500 / dbu
        polygon = pya.Polygon([
            pya.Point(-r, -r),
            pya.Point(-r, r),
            pya.Point(r, r),
            pya.Point(r, -r)
        ])
        polygon = polygon.round_corners(0, r, 128)
        rectangle = pya.Polygon([
            pya.Point(-r, 0),
            pya.Point(-r, r),
            pya.Point(r, r),
            pya.Point(r, 0)
        ])
        tt = pya.ICplxTrans(1, 90, False, 0, 0)
        qCircle = pya.Region(polygon) - pya.Region(rectangle) - pya.Region(
            rectangle.transform(tt))

        # Create the trapezoid
        trapezoid = pya.Polygon([
            pya.Point(49500 / dbu, -70500 / dbu),
            pya.Point(40500 / dbu, -20500 / dbu),
            pya.Point(60500 / dbu, -20500 / dbu),
            pya.Point(51500 / dbu, -70500 / dbu)
        ])
        tt = pya.ICplxTrans(-rx, -ry)

        cassette = qCircle + pya.Region(trapezoid.transform(tt))

        return cassette
Example #4
0
    def checkerboard(self, width, num=5):
        '''
    checkerboard(width, num)
    
    Generates a checkerboard pattern
    
    Parameters
    ---------
    width : integer
          The width of each square
    num : integer (5)
          The number of squares
    
    Returns
    ------
    region : pya.Region
         A region containing the checkerboard
    
    Description
    ---------
    A checkerboard pattern is used to qualitatively evaluate the resolution of the print.
    The corners of the checkboard pattern will degrade as resolution gets worse
    '''
        # Create a box
        square = pya.Polygon([
            pya.Point(0, 0),
            pya.Point(0, width),
            pya.Point(width, width),
            pya.Point(width, 0)
        ])
        polygons = []
        tc = pya.ICplxTrans(-int(num * width / 2), -int(num * width / 2))

        if (num % 2 == 1):
            for i in range(num * num):
                if (i % 2 == 0):
                    tt = pya.ICplxTrans(int(i % num * width),
                                        int(i / num * width))
                    polygons.append(tc.trans(tt.trans(square)))
        else:
            for i in range(num):
                for j in range(num):
                    if ((j + i) % 2 == 0):
                        tt = pya.ICplxTrans(int(i * width), int(j * width))
                        polygons.append(tc.trans(tt.trans(square)))

        return pya.Region(polygons)
Example #5
0
    def test_4_Trans(self):

        a = pya.Trans()
        m = pya.CplxTrans(a, 1.1)
        da = pya.DTrans()
        dm = pya.DCplxTrans(da, 1.1)

        self.assertEqual(str(m), "r0 *1.1 0,0")
        self.assertEqual(str(pya.DCplxTrans.from_s(str(m))), str(m))
        self.assertEqual(str(m.trans(pya.Point(5, -7))), "5.5,-7.7")

        im = pya.ICplxTrans(a, 0.5)
        im_old = im.dup()

        self.assertEqual(str(im), "r0 *0.5 0,0")
        self.assertEqual(str(pya.ICplxTrans.from_s(str(im))), str(im))
        self.assertEqual(str(im.trans(pya.Point(5, -7))), "3,-4")

        im = pya.ICplxTrans(m)
        self.assertEqual(str(im), "r0 *1.1 0,0")
        self.assertEqual(str(im.trans(pya.Point(5, -7))), "6,-8")

        im = pya.ICplxTrans(dm)
        self.assertEqual(str(im), "r0 *1.1 0,0")
        self.assertEqual(str(im.trans(pya.Point(5, -7))), "6,-8")

        im.assign(im_old)
        self.assertEqual(str(im), "r0 *0.5 0,0")
        self.assertEqual(str(im.trans(pya.Point(5, -7))), "3,-4")

        self.assertEqual(str(pya.ICplxTrans(5, -7)), "r0 *1 5,-7")

        self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans.R180, 1.5, 5, -7)),
                         "r180 *1.5 5,-7")
        self.assertEqual(
            str(pya.ICplxTrans(pya.ICplxTrans.R180, 1.5, pya.Point(5, -7))),
            "r180 *1.5 5,-7")
        self.assertEqual(
            str(pya.ICplxTrans(pya.ICplxTrans.R180, 1.5, pya.Vector(5, -7))),
            "r180 *1.5 5,-7")
        self.assertEqual(
            str(pya.ICplxTrans(pya.ICplxTrans.R180, 1.5, pya.DVector(5, -7))),
            "r180 *1.5 5,-7")
        self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans.R180, 1.5)),
                         "r180 *1.5 0,0")

        c = pya.ICplxTrans.from_dtrans(pya.DCplxTrans.M135)
        self.assertEqual(str(c), "m135 *1 0,0")
        c = pya.ICplxTrans.from_trans(pya.CplxTrans.M135)
        self.assertEqual(str(c), "m135 *1 0,0")
Example #6
0
  def siWafer(self, diameter, primaryFlat, secondaryFlat, angle, vertices = 128):
      '''
      siWafer(diameter, secondaryFlatAngle)
      
      Generates a Silicon Wafer shape
      
      Parameters
      ---------
      diameter : integer
            The diameter of a standard silicon wafer
      primaryFlat : integer
            The length of the primary flat
      secondaryFlat : integer
            The length of the secondary flat
      angle : double
            The location of the secondary flat relative (counterclockwise) to primary flat
      vertices : integer (coerce to even number)
            The number of vertices used to generate the circle
      
      Returns
      ------
      region : [pya.Region]
           A region containing the Si Wafer shape
      
      Description
      ---------
      SEMI Wafer Flat M1-0302 Specification
      Wafer Size  = [2", 3", 100mm, 125mm, 150mm, 200mm, 300mm]
      Diameter [mm] = [50.8, 76.2, 100, 125, 150, 200, 300]
      Thickness [um] = [279, 381, 525 or 625, 625, 675 or 625, 725, 775]
      Primary Flat Length = [15.88, 22.22, 32.5, 42.5, 57.5, Notch, Notch]
      Secondary Flat Length = [8, 11.18, 18, 27.5, 37.5, NA, NA]
      '''
      dList = [50800, 76200, 10000, 12500, 15000]
      pFlatLengthList = [15.88, 22.22, 32.5, 42.5, 57.5]
      sFlatLengthList = [8, 11.18, 18, 27.5, 37.5]
      
      r = int(diameter/2)
      
      #Height of arc position (https://mathworld.wolfram.com/CircularSegment.html)
      pH = r- int(np.sqrt(4*np.power(r,2)-np.power(primaryFlat,2))/2)
      sH = r - int(np.sqrt(4*np.power(r,2)-np.power(secondaryFlat,2))/2)
      
      # Create a circle
      polygon = pya.Polygon([pya.Point(-r,-r), pya.Point(-r,r), pya.Point(r,r), pya.Point(r,-r)])
      polygon = polygon.round_corners(0,r,vertices)
      
      #Create a rectangle to produce the primary flat
      pRectangle = pya.Polygon([pya.Point(-r,r-pH), pya.Point(-r,r+pH), pya.Point(r,r+pH), pya.Point(r,r-pH)])
      
      #Create a rectangle to produce the secondary flat
      sRectangle = pya.Polygon([pya.Point(-r,r-sH), pya.Point(-r,r+sH), pya.Point(r,r+sH), pya.Point(r,r-sH)])
      tt = pya.ICplxTrans(1, angle, False, 0, 0)

      return pya.Region(polygon)-pya.Region(pRectangle)-pya.Region(sRectangle.transform(tt))
Example #7
0
    def move_instance(self,
                      ind: int,
                      trans: 'pya.ICplxTrans',
                      mirror: bool = False):
        """Moves an InstanceHolder object

        :param ind: id of the InstanceHolder
        :param trans: list of transformations
        :param mirror: bool whether to mirror the object
        """
        trans = pya.ICplxTrans(1, trans[2], mirror, trans[0], trans[1])
        self.add_transformation(trans, ind)
Example #8
0
def create(lx, ly, ang, p, dc):
    """Create a square grating.

    lx:     length in x-direction
    ly:     length in y-direction
    ang:    rotation angle (degrees)
    p:      period of grating
    dc:     duty cycle of grating
    """

    reg = pya.Region()
    reg.insert(pya.Box(-lx / 2, -ly / 2, lx / 2, ly / 2))
    gbar = pya.Region()
    greps = int((lx) // p)
    for k in range(greps):
        gbar.insert(pya.Box(p * (k + dc / 2), -ly, p * (k + (1 - dc / 2)), ly))
        gbar.insert(
            pya.Box(p * (-k - dc / 2), -ly, p * (-k - (1 - dc / 2)), ly))
    gbar.transform(pya.ICplxTrans(1, ang, False, 0, 0))
    reg.__iand__(gbar)
    return reg
Example #9
0
    def calculate_ports(self, instances: list):
        """Calculates port locations in the cell layout. This is to propagate the port locations upwards

        :param instances: list containing :class:`kppc.photonics.InstanceHolder`
        """
        porttrans = []
        if self.transformations != '':
            # Check child cells ports. If there are ports which are not populated by other ports, add them as own ports
            trans = [
                pya.ICplxTrans.from_s(i)
                for i in self.transformations.split(';')
            ]
            for i, inst in enumerate(instances):
                if not inst.params_mod[0]:
                    continue
                ports = [
                    pya.ICplxTrans.from_s(k.split(':')[0])
                    for k in inst.params_mod[0].split(';')
                ]
                lengths = [
                    k.split(':')[1] for k in inst.params_mod[0].split(';')
                ]
                for q, r in zip(ports, lengths):
                    porttrans.append([trans[i] * q, r])
        if self.portlist != '':
            # Add manually created Ports
            porttrans.extend(
                [[pya.ICplxTrans.from_s(p.split(':')[0]),
                  p.split(':')[1]] for p in self.portlist.split(';')])

        porttrans = sorted(porttrans, key=lambda x: (x[0].disp.x, x[0].disp.y))
        # Add the calculated ports as own ports
        self.portlist = ''
        M180 = pya.ICplxTrans(1, 180, True, 0, 0)
        for p in porttrans:
            if not ([p[0] * pya.ICplxTrans.R180, p[1]] in porttrans
                    or [p[0] * M180, p[1]] in porttrans):
                if self.portlist != '':
                    self.portlist += ';'
                self.portlist += str(p[0]) + ':' + p[1]
Example #10
0
    def produce_impl(self):

        # Gets the reference to the cell view
        cv = pya.CellView().active()
        #Gets the reference to the layout
        layout = cv.layout()
        #Gets the databause unit
        dbu = self.layout.dbu

        # Create a temporary cell
        cell = layout.create_cell("tmp_cell")
        # Copy the target cell to the temporary cell
        cell.copy_tree(layout.cell(self.cellName))
        # Flatten the temporary cell
        cell.flatten(True)
        # Retrieve all shapes from the temporary cell
        shapes = cell.shapes(layout.layer(self.layer))

        # Convert parameters from [um] to [dbu] or database units
        px = int(self.pitchX / dbu)
        py = int(self.pitchY / dbu)

        # Create the dose series
        for i in range(self.nX):
            for j in range(self.nY):
                # Create a new datatype for each dose
                layer = self.layout.layer(int(self.layer.layer),
                                          i * self.nY + j)
                # Create a position for each dose
                tt = pya.ICplxTrans(px * j, py * i)
                # Insert the shapes into the PCell with the new layer and position
                self.cell.shapes(layer).insert(shapes, tt)

        # Delete the temporary cell
        cell.delete()
        # Make the newly added layers visible
        pya.LayoutView.current().add_missing_layers()
Example #11
0
    def DrawText_LiftOff(self, cell, layer1, teststr, DCplxTrans1):
        '''
        左下角坐标,每个字宽0.6*倍数高0.7*倍数线宽0.1*倍数  
        tr=pya.DCplxTrans(10,0,False,0,0)
        倍数,逆时针度数,是否绕x翻转,平移x,平移y
        '''
        reverse = False
        cellname = "TEXT(\"%s\")" % (teststr[:30].replace('\n', 'N'))
        charset = self.charset
        tr = pya.CplxTrans.from_dtrans(DCplxTrans1)

        filename = self.charfile
        layout = pya.Layout()
        layout.read(filename)
        cellList = [ii for ii in layout.top_cells()]

        layerList = self.charLayerList
        _layerlist = []
        for ii in layerList:
            _layerlist.append(layout.find_layer(ii[0], ii[1]))
        layers = [
            index for index in layout.layer_indices() if index in _layerlist
        ]

        fullregion = pya.Region()
        for layer_ in layers:
            fullregion.insert(cellList[0].begin_shapes_rec(layer_))
        fullregion.merge()

        charshapes = []
        for ii in range(len(charset)):
            subregion = pya.Region(pya.Box(ii * 600, 0, ii * 600 + 500, 700))
            if not reverse:
                subregion = subregion & fullregion
            else:
                subregion = subregion - fullregion
            subregion.merge()
            subregion.transform(pya.ICplxTrans(1, 0, False, -ii * 600, 0))
            charshapes.append(subregion)
            # cell.shapes(layer1).insert(subregion)
            pass

        ncell = IO.layout.create_cell(cellname)
        cell.insert(pya.CellInstArray(ncell.cell_index(), tr))

        currentx = 0
        currenty = 0

        for cc in teststr.upper():
            if cc == '\n':
                currenty += 1
                currentx = 0
                continue
            if cc in charset:
                ii = charset.index(cc)
                ncell.shapes(layer1).insert(charshapes[ii].transformed(
                    pya.ICplxTrans(1, 0, False, currentx * 600,
                                   -currenty * 800)))
                currentx += 1
                continue
            if cc in '\r\t\b\f':
                continue
            raise RuntimeError(f'"{cc}" is not supported')
Example #12
0
def rotate(theta=360 / n_sides):
    return pya.ICplxTrans(1, theta, False, 0, 0)
Example #13
0
    def cross(self, width, length, crossType=0):
        '''
    cross(width, length)
    
    Generates a cross
    
    Parameters
    ---------
    width : integer
          The width of each leg of the cross
    length : integer
          The length of each leg of the cross
    crossType : integer
          The type of cross
          0 : A regular cross
          1 : A dashed cross with center
          2 : A dashed cross without center
    
    Returns
    ------
    region : pya.Region
         A region containing the cross
    
    Description
    ---------
    A cross is a common pattern used for alignment.
    
    In photolithography a cross (width = 10, length = 100, unit = micron) can be used for coarse alignment.
    
    In ebeam lithography a cross (width = 3, length = 1000, unit = micron) can be used for global/coarse alignment
    and a cross (width = 3, length = 60, unit = micron) can be used for local/fine alignment. Typically, the cross pattern
    is made of 100 to 200 nm of dense material such Cr(10nm)/Au, Ti(10nm)/W, Ti(10nm)/Pt to provide ebeam
    contrast from the substrate.
    '''
        wh = int(width / 2)
        lh = int(length / 2)

        #Create a cross
        if (crossType == 0):
            #Create a rectangle with length along X
            xBox = pya.Box(-wh, -lh, wh, lh)
            #Create a rectangle with length along y
            yBox = pya.Box(-lh, -wh, lh, wh)
            #Combine the two rectangles together
            region = pya.Region(xBox) + pya.Region(yBox)
            #Merge the region
            region.merge()
        #Create a dashed cross with center
        elif (crossType == 1):
            #Define the position of the segments
            pos = [-4, -2, 2, 4]
            #Calculate segment dimension
            s = int((lh - wh) / 4)
            sh = int(s / 2)
            #Create segments
            xBox = pya.Box(-sh, -wh, sh, wh)
            yBox = pya.Box(-wh, -sh, wh, sh)
            #Define a region with a center cross
            region = pya.Region(pya.Box(-wh, -wh, wh, wh))
            #Calculate an offset due to center
            dw = sh - wh
            for i in pos:
                #Determine translations for negative positions
                if (i < 0):
                    tx = pya.ICplxTrans(int(i * s + dw), 0)
                    ty = pya.ICplxTrans(0, int(i * s + dw))
                else:
                    #Determine translations for positive positions
                    tx = pya.ICplxTrans(int(i * s - dw), 0)
                    ty = pya.ICplxTrans(0, int(i * s - dw))
                #Insert the segments into the region
                region.insert(tx.trans(xBox))
                region.insert(ty.trans(yBox))
        #Create a dashed cross without a center
        elif (crossType == 2):
            #Define the position of the segments
            pos = [-3, -1, 1, 3]
            #Calculate the segment dimension
            s = int((lh - wh) / 4)
            sh = int(s / 2)
            #Create segments
            xBox = pya.Box(-sh, -wh, sh, wh)
            yBox = pya.Box(-wh, -sh, wh, sh)
            #Calculate an offset due to center
            dw = sh - wh
            #Define a region with nothing
            region = pya.Region()
            for i in pos:
                if (i < 0):
                    #Determine translations for negative positions
                    tx = pya.ICplxTrans(int(i * s + dw), 0)
                    ty = pya.ICplxTrans(0, int(i * s + dw))
                else:
                    #Determine the translations for positive positions
                    tx = pya.ICplxTrans(int(i * s - dw), 0)
                    ty = pya.ICplxTrans(0, int(i * s - dw))
                #Insert the segments into the region
                region.insert(tx.trans(xBox))
                region.insert(ty.trans(yBox))

        return region
Example #14
0
def testKLayout():
    #This performs a simple test of the class and draws the result on the active layout in KLayout

    #Gets a reference to the active cell view
    cv = pya.CellView().active()

    #Gets a reference to the active layout
    layout = cv.layout()

    #Gets a reference or creates layer 1/0
    layer = layout.layer(1, 0)

    #Creates a new cell called TestShape
    if (layout.cell('TestShape') == None):
        cell = layout.create_cell('TestShape')
    #Clears the cell TestShape if it already exists
    else:
        cell = layout.cell('TestShape')
        cell.clear()

    #Gets the database units
    dbu = layout.dbu

    #Creates a shape object
    a = shape()

    #Creates a cross and insert it into the test cell
    region = a.cross(10 / dbu, 100 / dbu)
    cell.shapes(layer).insert(region)

    #Creates an inverted cross and insert it into the test cell
    region = a.cross(14 / dbu, 104 / dbu)
    region = a.invert(region, 10 / dbu)
    cell.shapes(layer).insert(region)

    #Creates a dashed cross
    tt = pya.ICplxTrans(0, -200000)
    region = a.cross(10 / dbu, 50 / dbu, 1)
    cell.shapes(layer).insert(region, tt)
    region = a.cross(10 / dbu, 50 / dbu, 2)
    cell.shapes(layer).insert(region, tt)
    tt = pya.ICplxTrans(-100000, -200000)
    region = a.cross(10 / dbu, 100 / dbu, 1)
    cell.shapes(layer).insert(region, tt)
    region = a.cross(10 / dbu, 100 / dbu, 2)
    cell.shapes(layer).insert(region, tt)

    #Creates a vernier pattern and inserts it into the test cell
    tt = pya.ICplxTrans(-100000, 0)
    region = a.vernier(4 / dbu, 40 / dbu, 8.2 / dbu)
    region.transform(tt)
    cell.shapes(layer).insert(region)

    #Creates an invert vernier pattern and inserts it into the test cell
    tt = pya.Trans(2, False, -160000, 0)
    region = a.vernier(4 / dbu, 40 / dbu, 8 / dbu)
    region = a.invert(region, 10 / dbu)
    cell.shapes(layer).insert(region, tt)

    tt = pya.ICplxTrans(0, 80000)
    region = a.text("0123456789 Hello")
    cell.shapes(layer).insert(region, tt)

    tt = pya.ICplxTrans(0, 100000)
    region = a.text("0123456789")
    region = a.invert(region, 10 / dbu)
    cell.shapes(layer).insert(region, tt)

    #Create checkboard patterns
    tt = pya.ICplxTrans(-100000, 150000)
    region = a.checkerboard(10 / dbu, 4)
    cell.shapes(layer).insert(region, tt)
    tt = pya.ICplxTrans(0, 150000)
    region = a.checkerboard(10 / dbu, 5)
    cell.shapes(layer).insert(region, tt)

    #Create Si Wafer patterns
    tt = pya.ICplxTrans(-100000, 200000)
    region = a.siWafer(50.8 / dbu, 15.88 / dbu, 8 / dbu, 90, 128)
    cell.shapes(layer).insert(region, tt)
    tt = pya.ICplxTrans(0, 200000)
    region = a.siWafer(50.8 / dbu, 15.88 / dbu, 8 / dbu, 45, 128)
    cell.shapes(layer).insert(region, tt)

    #Create cassette patterns
    tt = pya.ICplxTrans(100000, 0)
    region = a.pieceHolderCassette()
    cell.shapes(layer).insert(region, tt)

    #Create circle patterns
    tt = pya.ICplxTrans(-100000, 250000)
    region = a.circle(50 / dbu, 16)
    cell.shapes(layer).insert(region, tt)

    #Create ring patterns
    tt = pya.ICplxTrans(0, 250000)
    region = a.ring(50 / dbu, 25 / dbu, 32)
    cell.shapes(layer).insert(region, tt)
    tt = pya.ICplxTrans(100000, 250000)
    region = a.ring(50 / dbu, 25 / dbu, 32, False)
    cell.shapes(layer).insert(region, tt)

    cv.cell = cell
    def calculate_ports(self, instances: list):
        """Calculates port locations in the cell layout. This is to propagate the port locations upwards

        :param instances: list containing :class:`kppc.photonics.InstanceHolder`
        """
        porttrans = []
        if self.transformations != '':
            # Check child cells ports. If there are ports which are not populated by other ports, add them as own ports
            trans = [
                pya.ICplxTrans.from_s(i)
                for i in self.transformations.split(';')
            ]
            for i, inst in enumerate(instances):
                if not inst.params_mod[0]:
                    continue
                ports = [
                    pya.ICplxTrans.from_s(k.split(':')[0])
                    for k in inst.params_mod[0].split(';')
                ]
                lengths = [
                    k.split(':')[1] for k in inst.params_mod[0].split(';')
                ]
                names = [
                    k.split(':')[2] for k in inst.params_mod[0].split(';')
                ]
                for q, w, n in zip(ports, lengths, names):
                    porttrans.append([trans[i] * q, w, n])
        if self.portlist != '':
            # Add manually created Ports
            porttrans.extend([[
                pya.ICplxTrans.from_s(p.split(':')[0]),
                p.split(':')[1],
                p.split(':')[2]
            ] for p in self.portlist.split(';')])

        porttrans = sorted(porttrans, key=lambda x: (x[0].disp.x, x[0].disp.y))
        # Add the calculated ports as own ports
        self.portlist = ''
        M180 = pya.ICplxTrans(1, 180, True, 0, 0)
        nn, ne, ns, nw = 0, 0, 0, 0

        def regrep(match, nn, ne, ns, nw):
            if match.group(1) == 'N':
                ret = f'{match.group(1)}{nn}'
                nn += 1
                return ret
            if match.group(1) == 'E':
                ret = f'{match.group(1)}{ne}'
                ne += 1
                return ret
            if match.group(1) == 'S':
                ret = f'{match.group(1)}{ns}'
                ns += 1
                return ret
            if match.group(1) == 'W':
                ret = f'{match.group(1)}{nw}'
                nw += 1
                return ret

        for p in porttrans:
            if not ([p[0] * pya.ICplxTrans.R180, p[1]] in porttrans
                    or [p[0] * M180, p[1]] in porttrans):
                if self.portlist != '':
                    self.portlist += ';'
                rawname = p[2]
                regex = r"([NSEW])(?:\d+|{n})$"
                name = re.sub(regex, lambda x: regrep(x, nn, ne, ns, nw),
                              rawname)

                if name == rawname:
                    if 'N{n}' in rawname:
                        name = rawname.replace('{n}', str(nn))
                        nn += 1
                    elif 'E{n}' in rawname:
                        name = rawname.replace('{n}', str(ne))
                        ne += 1
                    elif 'W{n}' in rawname:
                        name = rawname.replace('{n}', str(nw))
                        nw += 1
                    elif 'S{n}' in rawname:
                        name = rawname.replace('{n}', str(ns))
                        ns += 1
                self.portlist += str(p[0]) + ':' + p[1] + ':' + name
Example #16
0
    def test_3_DTrans(self):

        c = pya.DCplxTrans(5.0, -7.0)
        self.assertEqual(str(c), "r0 *1 5,-7")

        c = pya.DCplxTrans(pya.DCplxTrans.M135)
        self.assertEqual(str(c), "m135 *1 0,0")
        self.assertEqual(c.is_unity(), False)
        self.assertEqual(c.is_ortho(), True)
        self.assertEqual(c.is_mag(), False)
        self.assertEqual(c.is_mirror(), True)
        self.assertEqual(c.rot(), pya.DCplxTrans.M135.rot())
        self.assertEqual(str(c.s_trans()), "m135 0,0")
        self.assertAlmostEqual(c.angle, 270)

        self.assertEqual(str(c.trans(pya.DEdge(0, 1, 2, 3))), "(-3,-2;-1,0)")
        self.assertEqual(str((c * pya.DEdge(0, 1, 2, 3))), "(-3,-2;-1,0)")
        self.assertEqual(str(c.trans(pya.DBox(0, 1, 2, 3))), "(-3,-2;-1,0)")
        self.assertEqual(str((c * pya.DBox(0, 1, 2, 3))), "(-3,-2;-1,0)")
        self.assertEqual(str(c.trans(pya.DText("text", pya.DVector(0, 1)))),
                         "('text',m135 -1,0)")
        self.assertEqual(str((c * pya.DText("text", pya.DVector(0, 1)))),
                         "('text',m135 -1,0)")
        self.assertEqual(
            str(
                c.trans(
                    pya.DPolygon([
                        pya.DPoint(0, 1),
                        pya.DPoint(2, -3),
                        pya.DPoint(4, 5)
                    ]))), "(-5,-4;-1,0;3,-2)")
        self.assertEqual(
            str((c * pya.DPolygon(
                [pya.DPoint(0, 1),
                 pya.DPoint(2, -3),
                 pya.DPoint(4, 5)]))), "(-5,-4;-1,0;3,-2)")
        self.assertEqual(
            str(c.trans(pya.DPath(
                [pya.DPoint(0, 1), pya.DPoint(2, 3)], 10))),
            "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false")
        self.assertEqual(
            str((c * pya.DPath(
                [pya.DPoint(0, 1), pya.DPoint(2, 3)], 10))),
            "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false")

        c = pya.DCplxTrans.from_itrans(pya.CplxTrans.M135)
        self.assertEqual(str(c), "m135 *1 0,0")

        c = pya.DCplxTrans(1.5)
        self.assertEqual(str(c), "r0 *1.5 0,0")
        self.assertEqual(c.is_unity(), False)
        self.assertEqual(c.is_ortho(), True)
        self.assertEqual(c.is_mag(), True)
        self.assertEqual(c.is_mirror(), False)
        self.assertEqual(c.rot(), pya.DCplxTrans.R0.rot())
        self.assertEqual(str(c.s_trans()), "r0 0,0")
        self.assertAlmostEqual(c.angle, 0)

        c = pya.DCplxTrans(0.75, 45, True, 2.5, -12.5)
        self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5")
        c = pya.DCplxTrans(0.75, 45, True, pya.DPoint(2.5, -12.5))
        self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5")
        self.assertEqual(c.is_unity(), False)
        self.assertEqual(c.is_ortho(), False)
        self.assertEqual(c.is_mag(), True)
        self.assertEqual(c.rot(), pya.DCplxTrans.M0.rot())
        self.assertEqual(str(c.s_trans()), "m0 2.5,-12.5")
        self.assertAlmostEqual(c.angle, 45)

        self.assertEqual(str(c.ctrans(5)), "3.75")
        self.assertEqual(str(c.trans(pya.DPoint(12, 16))),
                         "17.3492424049,-14.6213203436")

        self.assertEqual(str(pya.DCplxTrans()), "r0 *1 0,0")
        self.assertEqual(pya.DCplxTrans().is_unity(), True)
        self.assertEqual((c * c.inverted()).is_unity(), True)

        c.mirror = False
        self.assertEqual(str(c), "r45 *0.75 2.5,-12.5")
        c.mag = 1.5
        self.assertEqual(str(c), "r45 *1.5 2.5,-12.5")
        c.disp = pya.DPoint(-1.0, 5.5)
        self.assertEqual(str(c), "r45 *1.5 -1,5.5")
        self.assertEqual(c.mag, 1.5)
        c.angle = 60
        self.assertEqual(str(c), "r60 *1.5 -1,5.5")
        self.assertEqual(("%g" % c.angle), "60")

        # Constructor variations
        self.assertEqual(str(pya.ICplxTrans()), "r0 *1 0,0")
        self.assertEqual(str(pya.ICplxTrans(1.5)), "r0 *1.5 0,0")
        self.assertEqual(str(pya.ICplxTrans(pya.Trans(1, False, 10, 20), 1.5)),
                         "r90 *1.5 10,20")
        self.assertEqual(str(pya.ICplxTrans(pya.Trans(1, False, 10, 20))),
                         "r90 *1 10,20")
        self.assertEqual(
            str(pya.ICplxTrans(1.5, 80, True, pya.Vector(100, 200))),
            "m40 *1.5 100,200")
        self.assertEqual(str(pya.ICplxTrans(1.5, 80, True, 100, 200)),
                         "m40 *1.5 100,200")
        self.assertEqual(str(pya.ICplxTrans(pya.Vector(100, 200))),
                         "r0 *1 100,200")
        self.assertEqual(str(pya.ICplxTrans(100, 200)), "r0 *1 100,200")
        self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans(100, 200))),
                         "r0 *1 100,200")
        self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5)),
                         "r0 *1.5 150,300")
        self.assertEqual(
            str(
                pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5,
                               pya.Vector(10, 20))), "r0 *1.5 160,320")
        self.assertEqual(
            str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5, 10, 20)),
            "r0 *1.5 160,320")

        self.assertEqual(str(pya.DCplxTrans()), "r0 *1 0,0")
        self.assertEqual(str(pya.DCplxTrans(1.5)), "r0 *1.5 0,0")
        self.assertEqual(
            str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02), 1.5)),
            "r90 *1.5 0.01,0.02")
        self.assertEqual(str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02))),
                         "r90 *1 0.01,0.02")
        self.assertEqual(
            str(pya.DCplxTrans(1.5, 80, True, pya.DVector(0.1, 0.2))),
            "m40 *1.5 0.1,0.2")
        self.assertEqual(str(pya.DCplxTrans(1.5, 80, True, 0.1, 0.2)),
                         "m40 *1.5 0.1,0.2")
        self.assertEqual(str(pya.DCplxTrans(pya.DVector(0.1, 0.2))),
                         "r0 *1 0.1,0.2")
        self.assertEqual(str(pya.DCplxTrans(0.1, 0.2)), "r0 *1 0.1,0.2")
        self.assertEqual(str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2))),
                         "r0 *1 0.1,0.2")
        self.assertEqual(str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5)),
                         "r0 *1.5 0.15,0.3")
        self.assertEqual(
            str(
                pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5,
                               pya.DVector(0.01, 0.02))), "r0 *1.5 0.16,0.32")
        self.assertEqual(
            str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5, 0.01, 0.02)),
            "r0 *1.5 0.16,0.32")
Example #17
0
        def vernier_single_gen(param, t=pya.ICplxTrans(0, 0), dbu=1.0):
            """
            Returns list of polygons of vernier generated. Optionally with describtors
            
            Cleaner and more universal in comparism to previous pya.Cell atempt
                it doesn't require the layer and layout to be passed
            Parameters
            ----------
                param : obj
                    dataclass containing the vernier design parameters
                    class Vernier:
                        tLong  : float = 50.0           #Long tick lenght
                        tShort : float = 30.0           #Short tick lenght
                        tWidth : float = 5.0            #Tick wall width
                        sp : float    = 8.0             #Tick Spacing
                        tCnt : int     = 12             #Number of ticks overall
                        group : int    = 4              #Number of ticks per group (resolution)
                        markers : bool = False          #Allow markers above / bellow ticks 
                        asc : bool = False              #ascending of Label signs (+++/---)
                        centered : bool = True          #True: Center tick is 0, False: counts from side
                        markSize : float = 10.0         #Magnification of sign
                        markStp : int = 1               #Increment per tick
                        markTickSep : float = 5.0       #Separation between tick and marker 
            """
            tick_ln = pya.Box(-param.tWidth / 2 / dbu, 0.0,
                              param.tWidth / 2 / dbu, param.tLong / dbu)

            tick_sh = pya.Box(-param.tWidth / 2 / dbu, 0.0,
                              param.tWidth / 2 / dbu, param.tShort / dbu)

            #verniCell = pya.Cell()
            verni_polys = []

            #get the generator direction
            if param.centered:
                print(param.asc)
                if param.asc:
                    tick_range = range(-param.tCnt, param.tCnt + 1, +1)
                    dire = +1
                else:
                    tick_range = range(param.tCnt, -param.tCnt - 1, -1)
                    dire = -1
            else:
                if param.asc:
                    tick_range = range(-param.tCnt, 1, +1)
                    dire = +1
                else:
                    tick_range = range(param.tCnt, -1, -1)
                    dire = -1

            for loc in tick_range:
                #loc = tick_range[i]
                t_loc = pya.ICplxTrans(loc * param.sp / dbu,
                                       param.offset / dbu)
                if loc % param.group == 0:
                    #possition of long tick
                    tick_t = tick_ln.transformed(t_loc)
                    tick_t = tick_t.transformed(t)
                    poly = pya.Polygon(tick_t)
                    verni_polys.append(poly)

                    if param.markers:

                        gen = pya.TextGenerator.default_generator()
                        if (loc / param.group * param.markStp) == 0:
                            text = gen.text("0", dbu, param.markSize)
                        else:
                            text = gen.text(
                                "{:+.0f}".format(loc / param.group *
                                                 param.markStp), dbu,
                                param.markSize)

                        #text is generated with origin in the LB corner -> corecting by bbox size
                        t_loc_text = pya.ICplxTrans(
                            ((dire * loc * param.sp) / dbu -
                             (text.bbox().p2.x) / 2),
                            -((text.bbox().p2.y) + param.tLong / dbu +
                              param.markTickSep / dbu))

                        #print(t_loc_text)
                        text_t = text.transformed(t_loc_text)
                        t_text = t.to_trans()
                        #print(t_text)
                        #t_text.disp = t_text.disp / dbu
                        t_text.angle = t_text.angle + 180.0
                        #print(t_text)
                        #verni_polys.append(text_t)
                        verni_polys.append(text_t.transformed(t_text))

                        #TODO: generate marker labels
                else:
                    #position of short tick
                    tick_t = tick_sh.transformed(t_loc)
                    verni_polys.append(pya.Polygon(tick_t.transformed(t)))

            return verni_polys
Example #18
0
  def test_1_Polygon(self):

    a = pya.Polygon()
    self.assertEqual( str(a), "()" )
    self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) )
    self.assertEqual( a.is_box(), False )

    b = a.dup() 
    a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] )
    self.assertEqual( str(a), "(0,1;1,5;5,5)" )
    self.assertEqual( str(a * 2), "(0,2;2,10;10,10)" )
    self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) )
    self.assertEqual( a.num_points_hull(), 3 )
    c = a.dup() 

    self.assertEqual( a == b, False )
    self.assertEqual( a == c, True )
    self.assertEqual( a != b, True )
    self.assertEqual( a != c, False )

    a = pya.Polygon( pya.Box( 5, -10, 20, 15 ) )
    self.assertEqual( a.is_box(), True )
    self.assertEqual( str(a), "(5,-10;5,15;20,15;20,-10)" )
    self.assertEqual( str(pya.DPolygon(a)), "(5,-10;5,15;20,15;20,-10)" )
    self.assertEqual( a.num_points_hull(), 4 )
    self.assertEqual( a.area(), 15*25 )
    self.assertEqual( a.perimeter(), 80 )
    self.assertEqual( a.inside( pya.Point( 10, 0 ) ), True )
    self.assertEqual( a.inside( pya.Point( 5, 0 ) ), True )
    self.assertEqual( a.inside( pya.Point( 30, 0 ) ), False )

    arr = []
    for p in a.each_point_hull():
      arr.append(str(p))
    self.assertEqual( arr, ["5,-10", "5,15", "20,15", "20,-10"] )

    b = a.dup()

    self.assertEqual( str(a.moved( pya.Point( 0, 1 ) )), "(5,-9;5,16;20,16;20,-9)" )
    self.assertEqual( str(a.moved( 0, 1 )), "(5,-9;5,16;20,16;20,-9)" )
    aa = a.dup()
    aa.move( 1, 0 )
    self.assertEqual( str(aa), "(6,-10;6,15;21,15;21,-10)" )
    a.move( pya.Point( 1, 0 ) )
    self.assertEqual( str(a), "(6,-10;6,15;21,15;21,-10)" )

    b = b.transformed( pya.Trans( pya.Trans.R0, pya.Point( 1, 0 )) )
    self.assertEqual( str(b), "(6,-10;6,15;21,15;21,-10)" )

    m = pya.CplxTrans( pya.Trans(), 1.5 )
    self.assertEqual( str(a.transformed(m)), "(9,-15;9,22.5;31.5,22.5;31.5,-15)" )
    self.assertEqual( str(a.transformed(pya.ICplxTrans(m))), "(9,-15;9,23;32,23;32,-15)" )

    a.hull = [ pya.Point( 0, 1 ), pya.Point( 1, 1 ), pya.Point( 1, 5 ) ]
    self.assertEqual( str(a.bbox()), "(0,1;1,5)" )

    self.assertEqual( a.holes(), 0 )
    a.insert_hole( [ pya.Point( 1, 2 ), pya.Point( 2, 2 ), pya.Point( 2, 6 ) ] )
    self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" )
    self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) )
    self.assertEqual( a.area(), 0 )
    self.assertEqual( a.num_points_hole(0), 3 )
    self.assertEqual( a.holes(), 1 )
    self.assertEqual( str(a.point_hull(1)), "1,5" )
    self.assertEqual( str(a.point_hull(0)), "0,1" )
    self.assertEqual( str(a.point_hull(100)), "0,0" )
    self.assertEqual( str(a.point_hole(0, 100)), "0,0" )
    self.assertEqual( str(a.point_hole(0, 1)), "2,2" )
    self.assertEqual( str(a.point_hole(1, 1)), "0,0" )
    a.compress(False);
    self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" )
    a.compress(True);
    self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" )

    b = a.dup()
    b.assign_hole(0, pya.Box( 10, 20, 20, 60 ))
    self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60)" )
    self.assertEqual( b.is_box(), False )
    b.insert_hole(pya.Box( 10, 20, 20, 60 ))
    self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60/10,20;20,20;20,60;10,60)" )

    b = a.dup()
    b.assign_hole(0, [ pya.Point( 10, 20 ), pya.Point( 20, 20 ), pya.Point( 20, 60 ) ])
    self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" )
    b.assign_hole(1, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ])
    self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" )
    b.insert_hole( [ pya.Point( 1, 2 ), pya.Point( 2, 2 ), pya.Point( 2, 6 ) ] )
    self.assertEqual( str(b), "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" )
    b.assign_hole(0, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ])
    self.assertEqual( str(b), "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" )

    arr = []
    for p in a.each_point_hole(0):
      arr.append(str(p))
    self.assertEqual( arr, ["1,2", "2,2", "2,6"] )

    arr = []
    for p in a.each_edge():
      arr.append(str(p))
    self.assertEqual( arr, ["(0,1;1,5)", "(1,5;1,1)", "(1,1;0,1)", "(1,2;2,2)", "(2,2;2,6)", "(2,6;1,2)"] )

    a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] )
    self.assertEqual( str(a), "(0,1;1,5;5,5)" )
    self.assertEqual( str(a.sized(2)), "(0,-2;-2,0;-1,7;7,7;8,5)" )
    self.assertEqual( str(a.sized(2, 2)), "(0,-2;-2,0;-1,7;7,7;8,5)" )
    aa = a.dup()
    a.size(2, 2)
    self.assertEqual( str(a), "(0,-2;-2,0;-1,7;7,7;8,5)" )
    a = aa.dup()
    a.size(2)
    self.assertEqual( str(a), "(0,-2;-2,0;-1,7;7,7;8,5)" )

    a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] )
    self.assertEqual( str(a), "(0,1;1,5;5,5)" )
    self.assertEqual( str(a.sized(2, 0, 2)), "(-2,1;-1,5;7,5;2,1)" )
    a.size(2, 0, 2);
    self.assertEqual( str(a), "(-2,1;-1,5;7,5;2,1)" )

    a = pya.Polygon()
    self.assertEqual( str(a), "()" )

    # corner rounding
    a = pya.Polygon( [ pya.Point(0, 0), pya.Point(0, 2000), pya.Point(4000, 2000),
                             pya.Point(4000, 1000), pya.Point(2000, 1000), pya.Point(2000, 0) ] )
    ar = a.round_corners(100, 200, 8)
    self.assertEqual( str(ar), "(117,0;0,117;0,1883;117,2000;3883,2000;4000,1883;4000,1117;3883,1000;2059,1000;2000,941;2000,117;1883,0)" )
    ar = a.round_corners(200, 100, 32)
    self.assertEqual( str(ar), "(90,0;71,4;53,11;36,22;22,36;11,53;4,71;0,90;0,1910;4,1929;11,1947;22,1964;36,1978;53,1989;71,1996;90,2000;3910,2000;3929,1996;3947,1989;3964,1978;3978,1964;3989,1947;3996,1929;4000,1910;4000,1090;3996,1071;3989,1053;3978,1036;3964,1022;3947,1011;3929,1004;3910,1000;2180,1000;2142,992;2105,977;2073,955;2045,927;2023,895;2008,858;2000,820;2000,90;1996,71;1989,53;1978,36;1964,22;1947,11;1929,4;1910,0)" )

    # Minkowsky sums
    p = pya.Polygon( [ pya.Point.new(0, -100), pya.Point.new(0, -50), pya.Point.new(-100, -75), pya.Point.new(0, 100), pya.Point.new(50, 50), pya.Point.new(100, 75), pya.Point.new(100, 0), pya.Point.new(100, -50) ] )
    self.assertEqual(str(p.minkowsky_sum(pya.Edge.new(pya.Point.new(10, 10), pya.Point.new(210, 110)), True)), "(10,-90;10,-40;-90,-65;10,110;210,210;260,160;310,185;310,60)")
    self.assertEqual(str(p.minkowsky_sum([pya.Point.new(10, 10), pya.Point.new(10, 310), pya.Point.new(510, 310), pya.Point.new(510, 10), pya.Point.new(10, 10) ], False)), "(10,-90;10,-65;-90,-65;-90,235;10,410;510,410;535,385;610,385;610,-40;510,-90/110,110;410,110;410,210;110,210)")
    self.assertEqual(str(p.minkowsky_sum([pya.Point.new(10, 10), pya.Point.new(10, 310), pya.Point.new(510, 310), pya.Point.new(510, 10), pya.Point.new(10, 10) ], True)), "(10,-90;10,-65;-90,-65;-90,210;110,210;110,110;410,110;410,210;-90,210;-90,235;10,410;510,410;535,385;610,385;610,-40;510,-90)")
    self.assertEqual(str(p.minkowsky_sum(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 110)), True)), "(10,-90;10,-65;-90,-65;-90,35;10,210;210,210;235,185;310,185;310,-40;210,-90)")
    self.assertEqual(str(p.minkowsky_sum(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 10)), True)), "(10,-90;10,-65;-90,-65;10,110;210,110;235,85;310,85;310,-40;210,-90)")
    self.assertEqual(str(p.minkowsky_sum(pya.Polygon.new(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 110))), True)), "(10,-90;10,-65;-90,-65;-90,35;10,210;210,210;235,185;310,185;310,-40;210,-90)")

    # Smoothing
    p = pya.Polygon( [ pya.Point.new(0, 0), pya.Point.new(10, 50), pya.Point.new(0, 100), pya.Point.new(200, 100), pya.Point.new(200, 0) ])
    self.assertEqual(str(p.smooth(5)), "(0,0;10,50;0,100;200,100;200,0)")
    self.assertEqual(str(p.smooth(15)), "(0,0;0,100;200,100;200,0)")

    # Ellipse constructor
    p = pya.Polygon.ellipse( pya.Box(-10000, -20000, 30000, 40000), 200 )
    self.assertEqual(p.num_points(), 200)
    self.assertEqual(str(p.bbox()), "(-10000,-20000;30000,40000)")
    self.assertEqual(p.area(), 1884651158)    # roughly box.area*PI/4
    
    p = pya.Polygon.ellipse( pya.Box(-10000, -20000, 30000, 40000), 4 )
    self.assertEqual(str(p), "(10000,-20000;-10000,10000;10000,40000;30000,10000)")
Example #19
0
    def produce_impl(self):
        #Using Double values only - no dbu required (alternatively use scaling instead)
        #dbu definition is only for backwards compatibility with not "D" functions

        # @dataclass
        # class SqinSq:
        #     a : float       = 100.0         #Side lenght of the center square
        #     wall : float    = 10.0          #Wallthickness of the center square FM

        @dataclass
        class Vernier:
            tLong: float = 50.0  #Long tick lenght
            tShort: float = 30.0  #Short tick lenght
            tWidth: float = 5.0  #Tick wall width
            sp: float = 8.0  #Spacing first exposure
            offset: float = 0.0  #offset in y direction
            tCnt: int = 12  #Number of ticks per side (not inc. center)
            group: int = 4  #Number of ticks per group (resolution)
            markers: bool = False  #Allow markers above / bellow ticks
            asc: bool = False  #ascending of Label signs (+++/---)
            centered: bool = True  #True: Center tick is 0, False: counts from side
            markSize: float = 40.0  #Magnification of sign
            markStp: int = 1  #Increment number per long tick
            markTickSep: float = 5.0  #Separation bFalsetween tick and marker

        class HolowSq(pya.Polygon):
            """
            A class is child of pya -> DPolygon
            Builds a hollow square from 2 parameters
            ...

            Attributes
            ----------
            side : float
                first name of the person
            wall : float
                family name of the person

            Methods
            -------
            N/A
            """
            def __init__(self, side, wall, dbu=1):

                self.a = side / 2 / dbu
                self.h = side / 2 / dbu - wall / dbu

                self.assign(
                    pya.Polygon(pya.Box(-self.a, -self.a, self.a, self.a)))
                self.insert_hole(pya.Box(-self.h, -self.h, self.h, self.h))

        '''
        Constants and variables:
            DIST_VERNIER : float  - distance of vernier structures center from center of square
            DBU : float - database unit for backwards compatibility
            OL_OVERLAP: float - defines the FM and OL step vernier overlap (-) or gap (+)
            COMMENT_LOC : List : floats -  defines the location of optional comment under
                                            the marker
        '''
        DIST_VERNIER = 200
        DBU = 0.001
        OL_OVERLAP = 0
        COMMENT_LOC = [0, -350]
        ALIGN_LOC = [-DIST_VERNIER, DIST_VERNIER]
        ALIGN_TYPE = ["", "T\\nS\\nA", "B\\nS\\nA"]
        MARK_SIZE = [1000, 1000]

        sqFM = HolowSq(200.0, 20.0, DBU)
        sqOL = HolowSq(100.0, 20.0, DBU)
        sqArea = pya.Box(-MARK_SIZE[0] / 2 / DBU, -MARK_SIZE[0] / 2 / DBU,
                         MARK_SIZE[1] / 2 / DBU, MARK_SIZE[1] / 2 / DBU)

        _scale = pya.ICplxTrans(1 / DBU)

        verniL = Vernier(tLong=40.0,
                         tShort=30.0,
                         tWidth=5.0,
                         sp=13.0,
                         tCnt=12,
                         group=13)

        verniC_OL_B = Vernier(sp=13.25,
                              asc=True,
                              markers=True,
                              offset=OL_OVERLAP)

        verniC_OL_L = Vernier(sp=13.25,
                              asc=False,
                              markers=True,
                              offset=OL_OVERLAP)

        verniF_OL_R = Vernier(tWidth=5.0,
                              sp=15.1,
                              tCnt=10,
                              group=10,
                              markers=True,
                              asc=True,
                              offset=OL_OVERLAP)

        verniF_OL_T = Vernier(tWidth=5.0,
                              sp=15.1,
                              tCnt=10,
                              group=10,
                              markers=True,
                              asc=False,
                              offset=OL_OVERLAP)

        #Generate vernier array into the cell (layers would be taken from original parameters)
        def vernier_single_gen(param, t=pya.ICplxTrans(0, 0), dbu=1.0):
            """
            Returns list of polygons of vernier generated. Optionally with describtors
            
            Cleaner and more universal in comparism to previous pya.Cell atempt
                it doesn't require the layer and layout to be passed
            Parameters
            ----------
                param : obj
                    dataclass containing the vernier design parameters
                    class Vernier:
                        tLong  : float = 50.0           #Long tick lenght
                        tShort : float = 30.0           #Short tick lenght
                        tWidth : float = 5.0            #Tick wall width
                        sp : float    = 8.0             #Tick Spacing
                        tCnt : int     = 12             #Number of ticks overall
                        group : int    = 4              #Number of ticks per group (resolution)
                        markers : bool = False          #Allow markers above / bellow ticks 
                        asc : bool = False              #ascending of Label signs (+++/---)
                        centered : bool = True          #True: Center tick is 0, False: counts from side
                        markSize : float = 10.0         #Magnification of sign
                        markStp : int = 1               #Increment per tick
                        markTickSep : float = 5.0       #Separation between tick and marker 
            """
            tick_ln = pya.Box(-param.tWidth / 2 / dbu, 0.0,
                              param.tWidth / 2 / dbu, param.tLong / dbu)

            tick_sh = pya.Box(-param.tWidth / 2 / dbu, 0.0,
                              param.tWidth / 2 / dbu, param.tShort / dbu)

            #verniCell = pya.Cell()
            verni_polys = []

            #get the generator direction
            if param.centered:
                print(param.asc)
                if param.asc:
                    tick_range = range(-param.tCnt, param.tCnt + 1, +1)
                    dire = +1
                else:
                    tick_range = range(param.tCnt, -param.tCnt - 1, -1)
                    dire = -1
            else:
                if param.asc:
                    tick_range = range(-param.tCnt, 1, +1)
                    dire = +1
                else:
                    tick_range = range(param.tCnt, -1, -1)
                    dire = -1

            for loc in tick_range:
                #loc = tick_range[i]
                t_loc = pya.ICplxTrans(loc * param.sp / dbu,
                                       param.offset / dbu)
                if loc % param.group == 0:
                    #possition of long tick
                    tick_t = tick_ln.transformed(t_loc)
                    tick_t = tick_t.transformed(t)
                    poly = pya.Polygon(tick_t)
                    verni_polys.append(poly)

                    if param.markers:

                        gen = pya.TextGenerator.default_generator()
                        if (loc / param.group * param.markStp) == 0:
                            text = gen.text("0", dbu, param.markSize)
                        else:
                            text = gen.text(
                                "{:+.0f}".format(loc / param.group *
                                                 param.markStp), dbu,
                                param.markSize)

                        #text is generated with origin in the LB corner -> corecting by bbox size
                        t_loc_text = pya.ICplxTrans(
                            ((dire * loc * param.sp) / dbu -
                             (text.bbox().p2.x) / 2),
                            -((text.bbox().p2.y) + param.tLong / dbu +
                              param.markTickSep / dbu))

                        #print(t_loc_text)
                        text_t = text.transformed(t_loc_text)
                        t_text = t.to_trans()
                        #print(t_text)
                        #t_text.disp = t_text.disp / dbu
                        t_text.angle = t_text.angle + 180.0
                        #print(t_text)
                        #verni_polys.append(text_t)
                        verni_polys.append(text_t.transformed(t_text))

                        #TODO: generate marker labels
                else:
                    #position of short tick
                    tick_t = tick_sh.transformed(t_loc)
                    verni_polys.append(pya.Polygon(tick_t.transformed(t)))

            return verni_polys

        #lets store objects as regions
        obj_FM_layer = pya.Region()
        obj_OL_layer = pya.Region()
        obj_INV_layer = pya.Region()

        #First the squares
        # Square in square structure
        obj_FM_layer.insert(sqFM)
        obj_OL_layer.insert(sqOL)
        obj_INV_layer.insert(sqArea)
        #self.cell.shapes(self.l_layer).insert(sqFM)
        #self.cell.shapes(self.lo_layer).insert(sqOL)

        #depriciated
        verniers_poly = [[], []]

        #Lay step first first:
        rot_matrix = [[0, -1], [1, 0], [0, 1], [-1, 0]]

        #First layer vernier markers
        for i in range(0, 4):
            t = pya.ICplxTrans(1.0, 90 * i, False,
                               rot_matrix[i][0] * DIST_VERNIER / DBU,
                               rot_matrix[i][1] * DIST_VERNIER / DBU)
            verniers_poly[0].append(vernier_single_gen(verniL, t, DBU))

        #Overlay vernier markers with annotation
        t = pya.ICplxTrans(1.0, 180 + 90 * 0, False,
                           rot_matrix[0][0] * DIST_VERNIER / DBU,
                           rot_matrix[0][1] * DIST_VERNIER / DBU)
        verniers_poly[1].append(vernier_single_gen(verniC_OL_B, t, DBU))

        t = pya.ICplxTrans(1.0, 180 + 90 * 3, False,
                           rot_matrix[3][0] * DIST_VERNIER / DBU,
                           rot_matrix[3][1] * DIST_VERNIER / DBU)
        verniers_poly[1].append(vernier_single_gen(verniC_OL_L, t, DBU))

        t = pya.ICplxTrans(1.0, 180 + 90 * 1, False,
                           rot_matrix[1][0] * DIST_VERNIER / DBU,
                           rot_matrix[1][1] * DIST_VERNIER / DBU)
        verniers_poly[1].append(vernier_single_gen(verniF_OL_R, t, DBU))
        t = pya.ICplxTrans(1.0, 180 + 90 * 2, False,
                           rot_matrix[2][0] * DIST_VERNIER / DBU,
                           rot_matrix[2][1] * DIST_VERNIER / DBU)
        verniers_poly[1].append(vernier_single_gen(verniF_OL_T, t, DBU))

        for obj in verniers_poly[0]:
            for arr in obj:
                obj_FM_layer.insert(arr)

        for obj in verniers_poly[1]:
            for arr in obj:
                obj_OL_layer.insert(arr)

        #Generate names around the marker (e.g. 2A->1A)
        gen = pya.TextGenerator.default_generator()
        # positive
        stepText = gen.text(
            "{}\\n->\\n{}".format(self.step_name_L, self.step_name_OL), DBU,
            40, False, 0, 0, -1)
        #   positive to UR transformation
        t_stepText = pya.ICplxTrans(
            (DIST_VERNIER / DBU - stepText.bbox().p1.x),
            (DIST_VERNIER / DBU - stepText.bbox().p1.y))
        obj_FM_layer.insert(stepText.transform(t_stepText))

        stepTextInv = gen.text(
            "{}\\n->\\n{}".format(self.step_name_L, self.step_name_OL), DBU,
            40, True, 0, 0, -1)

        obj_OL_layer.insert(stepTextInv.transform(t_stepText))

        #put the alignment indication marker if prefered
        if self.align_type != 0:
            align_text = gen.text(ALIGN_TYPE[self.align_type], DBU, 40, False,
                                  0, 0, -1)
            align_textInv = gen.text(ALIGN_TYPE[self.align_type], DBU, 40,
                                     True, 0, 0, -1)
            #   positive to UR transformation
            t_align_text = pya.ICplxTrans(
                ALIGN_LOC[0] / DBU - align_text.bbox().p2.x,
                ALIGN_LOC[1] / DBU - align_text.bbox().p1.y)
            obj_FM_layer.insert(align_text.transform(t_align_text))
            obj_OL_layer.insert(align_textInv.transform(t_align_text))

        #comment section
        if self.comment != "":
            comment_text = gen.text(self.comment[:25], DBU, 40, False, 0, 0,
                                    -1)
            comment_textInv = gen.text(self.comment[:25], DBU, 40, True, 0, 0,
                                       -1)
            #   positive to UR transformation
            t_com_text = pya.ICplxTrans(
                (COMMENT_LOC[0] / DBU - comment_text.bbox().center().x),
                (COMMENT_LOC[1] / DBU - comment_text.bbox().center().y))
            obj_FM_layer.insert(comment_text.transform(t_com_text))
            obj_OL_layer.insert(comment_textInv.transform(t_com_text))

        #Tone destinqution:
        if self.fm_tone:
            # is possitive
            # used to be >> self.cell.shapes(self.l_layer).insert(obj_FM_layer.transform(_scale))
            self.cell.shapes(self.l_layer).insert(obj_FM_layer)
        else:
            print("tone FM negative")
            obj_neg = obj_INV_layer - obj_FM_layer
            self.cell.shapes(self.l_layer).insert(obj_neg)

        if self.ol_tone:
            # is possitive
            self.cell.shapes(self.lo_layer).insert(obj_OL_layer)
        else:
            print("tone OL negative")
            obj_neg = obj_INV_layer - obj_OL_layer
            self.cell.shapes(self.lo_layer).insert(obj_neg)

        if self.lb_allow:
            self.cell.shapes(self.lb_layer).insert(obj_INV_layer)