Example #1
0
    def __createFootprintVariant(self, device_params, device_dimensions,
                                 with_thermal_vias):
        fab_line_width = self.configuration.get('fab_line_width', 0.1)
        silk_line_width = self.configuration.get('silk_line_width', 0.12)

        lib_name = device_params.get('library', default_library)

        pincount = device_params['num_pins_x'] * 2 + device_params[
            'num_pins_y'] * 2

        default_ipc_config = 'qfn_pull_back' if 'lead_to_edge' in device_params else 'qfn'
        if device_params.get('ipc_class',
                             default_ipc_config) == 'qfn_pull_back':
            ipc_reference = 'ipc_spec_flat_no_lead_pull_back'
        else:
            ipc_reference = 'ipc_spec_flat_no_lead'

        used_density = device_params.get('ipc_density', ipc_density)
        ipc_data_set = self.ipc_defintions[ipc_reference][used_density]
        ipc_round_base = self.ipc_defintions[ipc_reference]['round_base']

        layout = ''
        if device_dimensions['has_EP']:
            name_format = self.configuration[
                'fp_name_EP_format_string_no_trailing_zero']
            if 'EP_size_x_overwrite' in device_params:
                EP_size = {
                    'x': device_params['EP_size_x_overwrite'],
                    'y': device_params['EP_size_y_overwrite']
                }
            else:
                EP_size = {
                    'x': device_dimensions['EP_size_x'].nominal,
                    'y': device_dimensions['EP_size_y'].nominal
                }
            EP_center = {
                'x': device_dimensions['EP_center_x'].nominal,
                'y': device_dimensions['EP_center_y'].nominal
            }
        else:
            name_format = self.configuration[
                'fp_name_format_string_no_trailing_zero']
            if device_params.get('use_name_format', 'QFN') == 'LGA':
                name_format = self.configuration[
                    'fp_name_lga_format_string_no_trailing_zero']
                if device_params['num_pins_x'] > 0 and device_params[
                        'num_pins_y'] > 0:
                    layout = self.configuration['lga_layout_border'].format(
                        nx=device_params['num_pins_x'],
                        ny=device_params['num_pins_y'])

            EP_size = {'x': 0, 'y': 0}

        if 'custom_name_format' in device_params:
            name_format = device_params['custom_name_format']

        pad_details = self.calcPadDetails(device_dimensions, EP_size,
                                          ipc_data_set, ipc_round_base)

        pad_suffix = '_Pad{pad_x:.2f}x{pad_y:.2f}mm'.format(
            pad_x=pad_details['left']['size'][0],
            pad_y=pad_details['left']['size'][1])
        pad_suffix = '' if device_params.get(
            'include_pad_size', 'none') not in ('fp_name_only',
                                                'both') else pad_suffix
        pad_suffix_3d = '' if device_params.get(
            'include_pad_size', 'none') not in ('both') else pad_suffix

        suffix = device_params.get('suffix', '')
        suffix_3d = suffix if device_params.get('include_suffix_in_3dpath',
                                                'True') == 'True' else ""

        model3d_path_prefix = self.configuration.get('3d_model_prefix',
                                                     '${KISYS3DMOD}')

        size_x = device_dimensions['body_size_x'].nominal
        size_y = device_dimensions['body_size_y'].nominal

        fp_name = name_format.format(
            man=device_params.get('manufacturer', ''),
            mpn=device_params.get('part_number', ''),
            pkg=device_params['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_dimensions['pitch'],
            layout=layout,
            ep_size_x=EP_size['x'],
            ep_size_y=EP_size['y'],
            suffix=pad_suffix,
            suffix2=suffix,
            vias=self.configuration.get('thermal_via_suffix', '_ThermalVias')
            if with_thermal_vias else '').replace('__', '_').lstrip('_')

        fp_name_2 = name_format.format(
            man=device_params.get('manufacturer', ''),
            mpn=device_params.get('part_number', ''),
            pkg=device_params['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_dimensions['pitch'],
            layout=layout,
            ep_size_x=EP_size['x'],
            ep_size_y=EP_size['y'],
            suffix=pad_suffix_3d,
            suffix2=suffix_3d,
            vias='').replace('__', '_').lstrip('_')

        if 'fp_name_prefix' in device_params:
            prefix = device_params['fp_name_prefix']
            if not prefix.endswith('_'):
                prefix += '_'
            fp_name = prefix + fp_name
            fp_name_2 = prefix + fp_name_2

        model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\
            .format(
                model3d_path_prefix=model3d_path_prefix, lib_name=lib_name,
                fp_name=fp_name_2)
        #print(fp_name)
        #print(pad_details)

        kicad_mod = Footprint(fp_name)

        # init kicad footprint
        kicad_mod.setDescription(
            "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\
            .format(
                manufacturer = device_params.get('manufacturer',''),
                package = device_params['device_type'],
                mpn = device_params.get('part_number',''),
                pincount = pincount,
                datasheet = device_params['size_source'],
                scriptname = os.path.basename(__file__).replace("  ", " ")
                ).lstrip())

        kicad_mod.setTags(self.configuration['keyword_fp_string']\
            .format(
                man=device_params.get('manufacturer',''),
                package=device_params['device_type'],
                category=category
            ).lstrip())
        kicad_mod.setAttribute('smd')

        pad_shape_details = {}
        pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT
        pad_shape_details['radius_ratio'] = configuration.get(
            'round_rect_radius_ratio', 0)
        if 'round_rect_max_radius' in configuration:
            pad_shape_details['maximum_radius'] = configuration[
                'round_rect_max_radius']

        if device_dimensions['has_EP']:
            if with_thermal_vias:
                thermals = device_params['thermal_vias']
                paste_coverage = thermals.get(
                    'EP_paste_coverage',
                    device_params.get('EP_paste_coverage',
                                      DEFAULT_PASTE_COVERAGE))

                kicad_mod.append(
                    ExposedPad(
                        number=pincount + 1,
                        size=EP_size,
                        at=EP_center,
                        paste_layout=thermals.get(
                            'EP_num_paste_pads',
                            device_params.get('EP_num_paste_pads', 1)),
                        paste_coverage=paste_coverage,
                        via_layout=thermals.get('count', 0),
                        paste_between_vias=thermals.get('paste_between_vias'),
                        paste_rings_outside=thermals.get(
                            'paste_rings_outside'),
                        via_drill=thermals.get('drill', 0.3),
                        via_grid=thermals.get('grid'),
                        paste_avoid_via=thermals.get('paste_avoid_via', True),
                        via_paste_clarance=thermals.get(
                            'paste_via_clearance',
                            DEFAULT_VIA_PASTE_CLEARANCE),
                        min_annular_ring=thermals.get(
                            'min_annular_ring', DEFAULT_MIN_ANNULAR_RING),
                        bottom_pad_min_size=thermals.get('bottom_min_size', 0),
                        kicad4_compatible=args.kicad4_compatible,
                        **pad_shape_details))
            else:
                kicad_mod.append(
                    ExposedPad(number=pincount + 1,
                               size=EP_size,
                               at=EP_center,
                               paste_layout=device_params.get(
                                   'EP_num_paste_pads', 1),
                               paste_coverage=device_params.get(
                                   'EP_paste_coverage',
                                   DEFAULT_PASTE_COVERAGE),
                               kicad4_compatible=args.kicad4_compatible,
                               **pad_shape_details))

        add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details,
                                    device_params)

        body_edge = {
            'left': -size_x / 2,
            'right': size_x / 2,
            'top': -size_y / 2,
            'bottom': size_y / 2
        }

        bounding_box = body_edge.copy()

        if device_params['num_pins_x'] == 0 and EP_size['y'] > size_y:
            bounding_box['top'] = -EP_size['y'] / 2
            bounding_box['bottom'] = EP_size['y'] / 2

        if device_params['num_pins_y'] == 0 and EP_size['x'] > size_x:
            bounding_box['left'] = -EP_size['x'] / 2
            bounding_box['right'] = EP_size['x'] / 2

        if device_params['num_pins_y'] > 0:
            bounding_box['left'] = pad_details['left']['center'][
                0] - pad_details['left']['size'][0] / 2

            bounding_box['right'] = pad_details['right']['center'][
                0] + pad_details['right']['size'][0] / 2

        if device_params['num_pins_x'] > 0:
            bounding_box['top'] = pad_details['top']['center'][
                1] - pad_details['top']['size'][1] / 2

            bounding_box['bottom'] = pad_details['bottom']['center'][
                1] + pad_details['bottom']['size'][1] / 2

        pad_width = pad_details['top']['size'][0]

        # ############################ SilkS ##################################

        silk_pad_offset = configuration[
            'silk_pad_clearance'] + configuration['silk_line_width'] / 2
        silk_offset = configuration['silk_fab_offset']
        if device_params['num_pins_x'] == 0:
            kicad_mod.append(
                Line(start={
                    'x': 0,
                    'y': body_edge['top'] - silk_offset
                },
                     end={
                         'x': body_edge['right'],
                         'y': body_edge['top'] - silk_offset
                     },
                     width=configuration['silk_line_width'],
                     layer="F.SilkS"))
            kicad_mod.append(
                Line(start={
                    'x': body_edge['left'],
                    'y': body_edge['bottom'] + silk_offset
                },
                     end={
                         'x': body_edge['right'],
                         'y': body_edge['bottom'] + silk_offset
                     },
                     width=configuration['silk_line_width'],
                     layer="F.SilkS",
                     y_mirror=0))
        elif device_params['num_pins_y'] == 0:
            kicad_mod.append(
                Line(start={
                    'y': 0,
                    'x': body_edge['left'] - silk_offset
                },
                     end={
                         'y': body_edge['bottom'],
                         'x': body_edge['left'] - silk_offset
                     },
                     width=configuration['silk_line_width'],
                     layer="F.SilkS"))
            kicad_mod.append(
                Line(start={
                    'y': body_edge['top'],
                    'x': body_edge['right'] + silk_offset
                },
                     end={
                         'y': body_edge['bottom'],
                         'x': body_edge['right'] + silk_offset
                     },
                     width=configuration['silk_line_width'],
                     layer="F.SilkS",
                     x_mirror=0))
        else:
            sx1 = -(device_dimensions['pitch'] *
                    (device_params['num_pins_x'] - 1) / 2.0 + pad_width / 2.0 +
                    silk_pad_offset)

            sy1 = -(device_dimensions['pitch'] *
                    (device_params['num_pins_y'] - 1) / 2.0 + pad_width / 2.0 +
                    silk_pad_offset)

            poly_silk = [{
                'x': sx1,
                'y': body_edge['top'] - silk_offset
            }, {
                'x': body_edge['left'] - silk_offset,
                'y': body_edge['top'] - silk_offset
            }, {
                'x': body_edge['left'] - silk_offset,
                'y': sy1
            }]
            if sx1 - SILK_MIN_LEN < body_edge['left'] - silk_offset:
                poly_silk = poly_silk[1:]
            if sy1 - SILK_MIN_LEN < body_edge['top'] - silk_offset:
                poly_silk = poly_silk[:-1]
            if len(poly_silk) > 1:
                kicad_mod.append(
                    PolygoneLine(polygone=poly_silk,
                                 width=configuration['silk_line_width'],
                                 layer="F.SilkS",
                                 x_mirror=0))
                kicad_mod.append(
                    PolygoneLine(polygone=poly_silk,
                                 width=configuration['silk_line_width'],
                                 layer="F.SilkS",
                                 y_mirror=0))
                kicad_mod.append(
                    PolygoneLine(polygone=poly_silk,
                                 width=configuration['silk_line_width'],
                                 layer="F.SilkS",
                                 x_mirror=0,
                                 y_mirror=0))
                if len(poly_silk) > 2:
                    kicad_mod.append(
                        Line(start={
                            'x': sx1,
                            'y': body_edge['top'] - silk_offset
                        },
                             end={
                                 'x': body_edge['left'] - silk_offset,
                                 'y': body_edge['top'] - silk_offset
                             },
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))

        # # ######################## Fabrication Layer ###########################

        fab_bevel_size = min(
            configuration['fab_bevel_size_absolute'],
            configuration['fab_bevel_size_relative'] * min(size_x, size_y))

        poly_fab = [
            {
                'x': body_edge['left'] + fab_bevel_size,
                'y': body_edge['top']
            },
            {
                'x': body_edge['right'],
                'y': body_edge['top']
            },
            {
                'x': body_edge['right'],
                'y': body_edge['bottom']
            },
            {
                'x': body_edge['left'],
                'y': body_edge['bottom']
            },
            {
                'x': body_edge['left'],
                'y': body_edge['top'] + fab_bevel_size
            },
            {
                'x': body_edge['left'] + fab_bevel_size,
                'y': body_edge['top']
            },
        ]

        kicad_mod.append(
            PolygoneLine(polygone=poly_fab,
                         width=configuration['fab_line_width'],
                         layer="F.Fab"))

        # # ############################ CrtYd ##################################

        off = ipc_data_set['courtyard']
        grid = configuration['courtyard_grid']

        cy1 = roundToBase(bounding_box['top'] - off, grid)

        kicad_mod.append(
            RectLine(start={
                'x': roundToBase(bounding_box['left'] - off, grid),
                'y': cy1
            },
                     end={
                         'x': roundToBase(bounding_box['right'] + off, grid),
                         'y': roundToBase(bounding_box['bottom'] + off, grid)
                     },
                     width=configuration['courtyard_line_width'],
                     layer='F.CrtYd'))

        # ######################### Text Fields ###############################

        addTextFields(kicad_mod=kicad_mod,
                      configuration=configuration,
                      body_edges=body_edge,
                      courtyard={
                          'top': cy1,
                          'bottom': -cy1
                      },
                      fp_name=fp_name,
                      text_y_inside_position='center')

        ##################### Output and 3d model ############################

        kicad_mod.append(Model(filename=model_name))

        output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
        if not os.path.isdir(
                output_dir
        ):  #returns false if path does not yet exist!! (Does not check path validity)
            os.makedirs(output_dir)
        filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir,
                                                            fp_name=fp_name)

        file_handler = KicadFileHandler(kicad_mod)
        file_handler.writeFile(filename)
    def __createFootprintVariant(self, device_params, header, dimensions, with_thermal_vias):
        fab_line_width = self.configuration.get('fab_line_width', 0.1)
        silk_line_width = self.configuration.get('silk_line_width', 0.12)

        lib_name = self.configuration['lib_name_format_string'].format(category=header['library_Suffix'])

        size_x = dimensions['body_size_x'].nominal
        size_y = dimensions['body_size_y'].nominal

        pincount = device_params['num_pins_x']*2 + device_params['num_pins_y']*2

        ipc_reference = 'ipc_spec_gw_large_pitch' if device_params['pitch'] >= 0.625 else 'ipc_spec_gw_small_pitch'
        if device_params.get('force_small_pitch_ipc_definition', False):
            ipc_reference = 'ipc_spec_gw_small_pitch'

        used_density = device_params.get('ipc_density', ipc_density)
        ipc_data_set = self.ipc_defintions[ipc_reference][used_density]
        ipc_round_base = self.ipc_defintions[ipc_reference]['round_base']

        pitch = device_params['pitch']

        name_format = self.configuration['fp_name_format_string_no_trailing_zero']
        EP_size = {'x':0, 'y':0}
        EP_mask_size = {'x':0, 'y':0}

        if dimensions['has_EP']:
            name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero']
            if 'EP_size_x_overwrite' in device_params:
                EP_size = {
                    'x':device_params['EP_size_x_overwrite'],
                    'y':device_params['EP_size_y_overwrite']
                    }
            else:
                EP_size = {
                    'x':dimensions['EP_size_x'].nominal,
                    'y':dimensions['EP_size_y'].nominal
                    }
            if 'EP_mask_x' in dimensions:
                name_format = self.configuration['fp_name_EP_custom_mask_format_string_no_trailing_zero']
                EP_mask_size = {'x':dimensions['EP_mask_x'].nominal, 'y':dimensions['EP_mask_y'].nominal}
        EP_size = Vector2D(EP_size)

        pad_details = self.calcPadDetails(dimensions, EP_size, ipc_data_set, ipc_round_base)

        if 'custom_name_format' in device_params:
            name_format = device_params['custom_name_format']

        suffix = device_params.get('suffix', '').format(pad_x=pad_details['left']['size'][0],
            pad_y=pad_details['left']['size'][1])
        suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else ""
        model3d_path_prefix = self.configuration.get('3d_model_prefix','${KISYS3DMOD}')

        fp_name = name_format.format(
            man=device_params.get('manufacturer',''),
            mpn=device_params.get('part_number',''),
            pkg=header['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            ep_size_x = EP_size['x'],
            ep_size_y = EP_size['y'],
            mask_size_x = EP_mask_size['x'],
            mask_size_y = EP_mask_size['y'],
            suffix=suffix,
            suffix2="",
            vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else ''
            ).replace('__','_').lstrip('_')

        fp_name_2 = name_format.format(
            man=device_params.get('manufacturer',''),
            mpn=device_params.get('part_number',''),
            pkg=header['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            ep_size_x = EP_size['x'],
            ep_size_y = EP_size['y'],
            mask_size_x = EP_mask_size['x'],
            mask_size_y = EP_mask_size['y'],
            suffix=suffix_3d,
            suffix2="",
            vias=''
            ).replace('__','_').lstrip('_')

        model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\
            .format(
                model3d_path_prefix=model3d_path_prefix, lib_name=lib_name,
                fp_name=fp_name_2)
        #print(fp_name)
        #print(pad_details)

        kicad_mod = Footprint(fp_name)

                # init kicad footprint
        kicad_mod.setDescription(
            "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\
            .format(
                manufacturer = device_params.get('manufacturer',''),
                package = header['device_type'],
                mpn = device_params.get('part_number',''),
                pincount = pincount,
                datasheet = device_params['size_source'],
                scriptname = os.path.basename(__file__).replace("  ", " ")
                ).lstrip())

        kicad_mod.setTags(self.configuration['keyword_fp_string']\
            .format(
                man=device_params.get('manufacturer',''),
                package=header['device_type'],
                category=header['library_Suffix']
            ).lstrip())
        kicad_mod.setAttribute('smd')

        pad_shape_details = {}
        pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT
        pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0)
        if 'round_rect_max_radius' in configuration:
            pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius']

        EP_round_radius = 0
        if dimensions['has_EP']:
            EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None

            if with_thermal_vias:
                thermals = device_params['thermal_vias']
                paste_coverage = thermals.get('EP_paste_coverage',
                                               device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE))

                EP = ExposedPad(
                    number=pincount+1, size=EP_size, mask_size=EP_mask_size,
                    paste_layout=thermals.get('EP_num_paste_pads'),
                    paste_coverage=paste_coverage,
                    via_layout=thermals.get('count', 0),
                    paste_between_vias=thermals.get('paste_between_vias'),
                    paste_rings_outside=thermals.get('paste_rings_outside'),
                    via_drill=thermals.get('drill', 0.3),
                    via_grid=thermals.get('grid'),
                    paste_avoid_via=thermals.get('paste_avoid_via', True),
                    via_paste_clarance=thermals.get('paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE),
                    min_annular_ring=thermals.get('min_annular_ring', DEFAULT_MIN_ANNULAR_RING),
                    bottom_pad_min_size=thermals.get('bottom_min_size', 0),
                    **pad_shape_details
                    )
            else:
                EP = ExposedPad(
                    number=pincount+1, size=EP_size, mask_size=EP_mask_size,
                    paste_layout=device_params.get('EP_num_paste_pads', 1),
                    paste_coverage=device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE),
                    **pad_shape_details
                    )

            kicad_mod.append(EP)
            EP_round_radius = EP.getRoundRadius()

        pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params)

        body_edge = {
            'left': -dimensions['body_size_x'].nominal/2,
            'right': dimensions['body_size_x'].nominal/2,
            'top': -dimensions['body_size_y'].nominal/2,
            'bottom': dimensions['body_size_y'].nominal/2
            }

        bounding_box = {
            'left': pad_details['left']['center'][0] - pad_details['left']['size'][0]/2,
            'right': pad_details['right']['center'][0] + pad_details['right']['size'][0]/2,
            'top': pad_details['top']['center'][1] - pad_details['top']['size'][1]/2,
            'bottom': pad_details['bottom']['center'][1] + pad_details['bottom']['size'][1]/2
        }

        if device_params['num_pins_x'] == 0:
            bounding_box['top'] = body_edge['top']
            bounding_box['bottom'] = body_edge['bottom']
            if EP_size['y'] > dimensions['body_size_y'].nominal:
                bounding_box['top'] = -EP_size['y']/2
                bounding_box['bottom'] = EP_size['y']/2

        if device_params['num_pins_y'] == 0:
            bounding_box['left'] = body_edge['left']
            bounding_box['right'] = body_edge['right']
            if EP_size['x'] > dimensions['body_size_x'].nominal:
                bounding_box['left'] = -EP_size['x']/2
                bounding_box['right'] = EP_size['x']/2


        pad_width = pad_details['top']['size'][0]

        # ############################ SilkS ##################################
        silk_pad_offset = configuration['silk_pad_clearance'] + configuration['silk_line_width']/2
        silk_offset = configuration['silk_fab_offset']

        right_pads_silk_bottom = (device_params['num_pins_y']-1)*device_params['pitch']/2\
            +pad_details['right']['size'][1]/2+silk_pad_offset
        silk_bottom = body_edge['bottom']+silk_offset
        if EP_size['y']/2 <= body_edge['bottom'] and right_pads_silk_bottom >= silk_bottom:
            silk_bottom = max(silk_bottom, EP_size['y']/2+silk_pad_offset)

        silk_bottom = max(silk_bottom, right_pads_silk_bottom)
        silk_bottom = min(body_edge['bottom']+silk_pad_offset, silk_bottom)

        bottom_pads_silk_right = (device_params['num_pins_x']-1)*device_params['pitch']/2\
            +pad_details['bottom']['size'][0]/2+silk_pad_offset
        silk_right = body_edge['right']+silk_offset
        if EP_size['x']/2 <= body_edge['right'] and bottom_pads_silk_right >= silk_right:
            silk_right = max(silk_right, EP_size['x']/2+silk_pad_offset)
        silk_right = max(silk_right, bottom_pads_silk_right)
        silk_right = min(body_edge['right']+silk_pad_offset, silk_right)


        min_lenght = configuration.get('silk_line_lenght_min', 0)
        silk_corner_bottom_right = Vector2D(silk_right, silk_bottom)

        silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine(
            pad_size=EP_size,
            pad_position=[0, 0],
            pad_radius=EP_round_radius,
            fixed_point=silk_corner_bottom_right,
            moving_point=Vector2D(0, silk_bottom),
            silk_pad_offset=silk_pad_offset,
            min_lenght=min_lenght)

        if silk_point_bottom_inside is not None and device_params['num_pins_x'] > 0:
            silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['bottom']['size'],
                pad_position=[
                    pad_details['bottom']['center'][0]+(device_params['num_pins_x']-1)/2*pitch,
                    pad_details['bottom']['center'][1]],
                pad_radius=pad_radius,
                fixed_point=silk_corner_bottom_right,
                moving_point=silk_point_bottom_inside,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        silk_point_right_inside = nearestSilkPointOnOrthogonalLine(
            pad_size=EP_size,
            pad_position=[0, 0],
            pad_radius=EP_round_radius,
            fixed_point=silk_corner_bottom_right,
            moving_point=Vector2D(silk_right, 0),
            silk_pad_offset=silk_pad_offset,
            min_lenght=min_lenght)
        if silk_point_right_inside is not None and device_params['num_pins_y'] > 0:
            silk_point_right_inside = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['right']['size'],
                pad_position=[
                    pad_details['right']['center'][0],
                    pad_details['right']['center'][1]+(device_params['num_pins_y']-1)/2*pitch],
                pad_radius=pad_radius,
                fixed_point=silk_corner_bottom_right,
                moving_point=silk_point_right_inside,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        if silk_point_bottom_inside is None and silk_point_right_inside is not None:
            silk_corner_bottom_right['y'] = body_edge['bottom']
            silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['bottom']['size'],
                pad_position=[
                    pad_details['bottom']['center'][0]+(device_params['num_pins_x']-1)/2*pitch,
                    pad_details['bottom']['center'][1]],
                pad_radius=pad_radius,
                fixed_point=silk_point_right_inside,
                moving_point=silk_corner_bottom_right,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        elif silk_point_right_inside is None and silk_point_bottom_inside is not None:
            silk_corner_bottom_right['x'] = body_edge['right']
            silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['right']['size'],
                pad_position=[
                    pad_details['right']['center'][0],
                    pad_details['right']['center'][1]+(device_params['num_pins_y']-1)/2*pitch],
                pad_radius=pad_radius,
                fixed_point=silk_point_bottom_inside,
                moving_point=silk_corner_bottom_right,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        poly_bottom_right = []
        if silk_point_bottom_inside is not None:
            poly_bottom_right.append(silk_point_bottom_inside)
        poly_bottom_right.append(silk_corner_bottom_right)
        if silk_point_right_inside is not None:
            poly_bottom_right.append(silk_point_right_inside)

        if len(poly_bottom_right) > 1 and silk_corner_bottom_right is not None:
            kicad_mod.append(PolygoneLine(
                polygone=poly_bottom_right,
                width=configuration['silk_line_width'],
                layer="F.SilkS"))
            kicad_mod.append(PolygoneLine(
                polygone=poly_bottom_right,
                width=configuration['silk_line_width'],
                layer="F.SilkS", x_mirror=0))
            kicad_mod.append(PolygoneLine(
                polygone=poly_bottom_right,
                width=configuration['silk_line_width'],
                layer="F.SilkS", y_mirror=0))

            if device_params['num_pins_y'] > 0:
                if len(poly_bottom_right)>2:
                    kicad_mod.append(PolygoneLine(
                        polygone=poly_bottom_right,
                        width=configuration['silk_line_width'],
                        layer="F.SilkS", y_mirror=0, x_mirror=0))
                    kicad_mod.append(Line(
                        start={'x': -silk_right, 'y': -right_pads_silk_bottom},
                        end={'x': bounding_box['left'], 'y': -right_pads_silk_bottom},
                        width=configuration['silk_line_width'],
                        layer="F.SilkS"))
                elif silk_corner_bottom_right['y'] >= right_pads_silk_bottom and silk_point_bottom_inside is not None:
                    kicad_mod.append(Line(
                        start=-silk_point_bottom_inside,
                        end={'x': bounding_box['left'], 'y': -silk_point_bottom_inside['y']},
                        width=configuration['silk_line_width'],
                        layer="F.SilkS"))
            else:
                if len(poly_bottom_right)>2:
                    poly_bottom_right[0]['x']=bottom_pads_silk_right
                    kicad_mod.append(PolygoneLine(
                        polygone=poly_bottom_right,
                        width=configuration['silk_line_width'],
                        layer="F.SilkS", y_mirror=0, x_mirror=0))
                    kicad_mod.append(Line(
                        start={'x': -bottom_pads_silk_right, 'y': -silk_corner_bottom_right['y']},
                        end={'x': -bottom_pads_silk_right, 'y': bounding_box['top']},
                        width=configuration['silk_line_width'],
                        layer="F.SilkS"))
                elif silk_corner_bottom_right['x'] >= bottom_pads_silk_right and silk_point_right_inside is not None:
                    kicad_mod.append(Line(
                        start=-silk_point_right_inside,
                        end={'x': -silk_point_right_inside['x'], 'y': bounding_box['top']},
                        width=configuration['silk_line_width'],
                        layer="F.SilkS"))

        # # ######################## Fabrication Layer ###########################

        fab_bevel_size = min(configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative']*min(size_x, size_y))

        poly_fab = [
            {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']},
            {'x': body_edge['right'], 'y': body_edge['top']},
            {'x': body_edge['right'], 'y': body_edge['bottom']},
            {'x': body_edge['left'], 'y': body_edge['bottom']},
            {'x': body_edge['left'], 'y': body_edge['top']+fab_bevel_size},
            {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']},
        ]

        kicad_mod.append(PolygoneLine(
            polygone=poly_fab,
            width=configuration['fab_line_width'],
            layer="F.Fab"))

        # # ############################ CrtYd ##################################

        off = ipc_data_set['courtyard']
        grid = configuration['courtyard_grid']

        if device_params['num_pins_y'] == 0 or device_params['num_pins_x'] == 0:
            cy1=roundToBase(bounding_box['top']-off, grid)

            kicad_mod.append(RectLine(
                start={
                    'x':roundToBase(bounding_box['left']-off, grid),
                    'y':cy1
                    },
                end={
                    'x':roundToBase(bounding_box['right']+off, grid),
                    'y':roundToBase(bounding_box['bottom']+off, grid)
                    },
                width=configuration['courtyard_line_width'],
                layer='F.CrtYd'))

        else:
            cy1=roundToBase(bounding_box['top']-off, grid)
            cy2=roundToBase(body_edge['top']-off, grid)
            cy3=-roundToBase(
                device_params['pitch']*(device_params['num_pins_y']-1)/2.0
                + pad_width/2.0 + off, grid)



            cx1=-roundToBase(
                device_params['pitch']*(device_params['num_pins_x']-1)/2.0
                + pad_width/2.0 + off, grid)
            cx2=roundToBase(body_edge['left']-off, grid)
            cx3=roundToBase(bounding_box['left']-off, grid)


            crty_poly_tl = [
                {'x':0, 'y':cy1},
                {'x':cx1, 'y':cy1},
                {'x':cx1, 'y':cy2},
                {'x':cx2, 'y':cy2},
                {'x':cx2, 'y':cy3},
                {'x':cx3, 'y':cy3},
                {'x':cx3, 'y':0}
            ]
            kicad_mod.append(PolygoneLine(polygone=crty_poly_tl,
                layer='F.CrtYd', width=configuration['courtyard_line_width']))
            kicad_mod.append(PolygoneLine(polygone=crty_poly_tl,
                layer='F.CrtYd', width=configuration['courtyard_line_width'],
                x_mirror=0))
            kicad_mod.append(PolygoneLine(polygone=crty_poly_tl,
                layer='F.CrtYd', width=configuration['courtyard_line_width'],
                y_mirror=0))
            kicad_mod.append(PolygoneLine(polygone=crty_poly_tl,
                layer='F.CrtYd', width=configuration['courtyard_line_width'],
                x_mirror=0, y_mirror=0))

        # ######################### Text Fields ###############################

        addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge,
            courtyard={'top': cy1, 'bottom': -cy1}, fp_name=fp_name, text_y_inside_position='center')

        ##################### Output and 3d model ############################

        kicad_mod.append(Model(filename=model_name))

        output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
        if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity)
            os.makedirs(output_dir)
        filename =  '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name)

        file_handler = KicadFileHandler(kicad_mod)
        file_handler.writeFile(filename)
    def __createFootprintVariant(self, device_params, header, dimensions,
                                 with_thermal_vias):
        fab_line_width = self.configuration.get('fab_line_width', 0.1)
        silk_line_width = self.configuration.get('silk_line_width', 0.12)

        lib_name = self.configuration['lib_name_format_string'].format(
            category=header['library_Suffix'])

        size_x = dimensions['body_size_x'].nominal
        size_y = dimensions['body_size_y'].nominal

        pincount = device_params['num_pins_x'] * 2 + device_params[
            'num_pins_y'] * 2

        ipc_reference = 'ipc_spec_gw_large_pitch' if device_params[
            'pitch'] >= 0.625 else 'ipc_spec_gw_small_pitch'

        ipc_data_set = self.ipc_defintions[ipc_reference][ipc_density]
        ipc_round_base = self.ipc_defintions[ipc_reference]['round_base']

        pitch = device_params['pitch']
        pad_details = self.calcPadDetails(dimensions, ipc_data_set,
                                          ipc_round_base)

        suffix = device_params.get('suffix', '').format(
            pad_x=pad_details['left']['size'][0],
            pad_y=pad_details['left']['size'][1])
        suffix_3d = suffix if device_params.get('include_suffix_in_3dpath',
                                                'True') == 'True' else ""
        model3d_path_prefix = self.configuration.get('3d_model_prefix',
                                                     '${KISYS3DMOD}')

        name_format = self.configuration[
            'fp_name_format_string_no_trailing_zero']
        EP_size = {'x': 0, 'y': 0}
        EP_mask_size = {'x': 0, 'y': 0}

        if dimensions['has_EP']:
            name_format = self.configuration[
                'fp_name_EP_format_string_no_trailing_zero']
            EP_size = {
                'x': dimensions['EP_size_x'].nominal,
                'y': dimensions['EP_size_y'].nominal
            }
            if 'EP_mask_x' in dimensions:
                name_format = self.configuration[
                    'fp_name_EP_custom_mask_format_string_no_trailing_zero']
                EP_mask_size = {
                    'x': dimensions['EP_mask_x'].nominal,
                    'y': dimensions['EP_mask_y'].nominal
                }
        EP_size = Vector2D(EP_size)

        if 'custom_name_format' in device_params:
            name_format = device_params['custom_name_format']

        fp_name = name_format.format(
            man=device_params.get('manufacturer', ''),
            mpn=device_params.get('part_number', ''),
            pkg=header['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            ep_size_x=EP_size['x'],
            ep_size_y=EP_size['y'],
            mask_size_x=EP_mask_size['x'],
            mask_size_y=EP_mask_size['y'],
            suffix=suffix,
            suffix2="",
            vias=self.configuration.get('thermal_via_suffix', '_ThermalVias')
            if with_thermal_vias else '').replace('__', '_').lstrip('_')

        fp_name_2 = name_format.format(
            man=device_params.get('manufacturer', ''),
            mpn=device_params.get('part_number', ''),
            pkg=header['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            ep_size_x=EP_size['x'],
            ep_size_y=EP_size['y'],
            mask_size_x=EP_mask_size['x'],
            mask_size_y=EP_mask_size['y'],
            suffix=suffix_3d,
            suffix2="",
            vias='').replace('__', '_').lstrip('_')

        model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\
            .format(
                model3d_path_prefix=model3d_path_prefix, lib_name=lib_name,
                fp_name=fp_name_2)
        #print(fp_name)
        #print(pad_details)

        kicad_mod = Footprint(fp_name)

        # init kicad footprint
        kicad_mod.setDescription(
            "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\
            .format(
                manufacturer = device_params.get('manufacturer',''),
                package = header['device_type'],
                mpn = device_params.get('part_number',''),
                pincount = pincount,
                datasheet = device_params['size_source'],
                scriptname = os.path.basename(__file__).replace("  ", " ")
                ).lstrip())

        kicad_mod.setTags(self.configuration['keyword_fp_string']\
            .format(
                man=device_params.get('manufacturer',''),
                package=header['device_type'],
                category=header['library_Suffix']
            ).lstrip())
        kicad_mod.setAttribute('smd')

        pad_shape_details = {}
        pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT
        pad_shape_details['radius_ratio'] = configuration.get(
            'round_rect_radius_ratio', 0)
        if 'round_rect_max_radius' in configuration:
            pad_shape_details['maximum_radius'] = configuration[
                'round_rect_max_radius']

        EP_round_radius = 0
        if dimensions['has_EP']:
            EP_mask_size = EP_mask_size if EP_mask_size['x'] > 0 else None

            if with_thermal_vias:
                thermals = device_params['thermal_vias']
                paste_coverage = thermals.get(
                    'EP_paste_coverage',
                    device_params.get('EP_paste_coverage',
                                      DEFAULT_PASTE_COVERAGE))

                EP = ExposedPad(
                    number=pincount + 1,
                    size=EP_size,
                    mask_size=EP_mask_size,
                    paste_layout=thermals.get('EP_num_paste_pads'),
                    paste_coverage=paste_coverage,
                    via_layout=thermals.get('count', 0),
                    paste_between_vias=thermals.get('paste_between_vias'),
                    paste_rings_outside=thermals.get('paste_rings_outside'),
                    via_drill=thermals.get('drill', 0.3),
                    via_grid=thermals.get('grid'),
                    paste_avoid_via=thermals.get('paste_avoid_via', True),
                    via_paste_clarance=thermals.get(
                        'paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE),
                    min_annular_ring=thermals.get('min_annular_ring',
                                                  DEFAULT_MIN_ANNULAR_RING),
                    bottom_pad_min_size=thermals.get('bottom_min_size', 0),
                    **pad_shape_details)
            else:
                EP = ExposedPad(
                    number=pincount + 1,
                    size=EP_size,
                    mask_size=EP_mask_size,
                    paste_layout=device_params.get('EP_num_paste_pads', 1),
                    paste_coverage=device_params.get('EP_paste_coverage',
                                                     DEFAULT_PASTE_COVERAGE),
                    **pad_shape_details)

            kicad_mod.append(EP)
            EP_round_radius = EP.getRoundRadius()

        pad_radius = add_dual_or_quad_pad_border(kicad_mod, configuration,
                                                 pad_details, device_params)

        body_edge = {
            'left': -dimensions['body_size_x'].nominal / 2,
            'right': dimensions['body_size_x'].nominal / 2,
            'top': -dimensions['body_size_y'].nominal / 2,
            'bottom': dimensions['body_size_y'].nominal / 2
        }

        bounding_box = {
            'left':
            pad_details['left']['center'][0] -
            pad_details['left']['size'][0] / 2,
            'right':
            pad_details['right']['center'][0] +
            pad_details['right']['size'][0] / 2,
            'top':
            pad_details['top']['center'][1] -
            pad_details['top']['size'][1] / 2,
            'bottom':
            pad_details['bottom']['center'][1] +
            pad_details['bottom']['size'][1] / 2
        }

        if device_params['num_pins_x'] == 0:
            bounding_box['top'] = body_edge['top']
            bounding_box['bottom'] = body_edge['bottom']
            if EP_size['y'] > dimensions['body_size_y'].nominal:
                bounding_box['top'] = -EP_size['y'] / 2
                bounding_box['bottom'] = EP_size['y'] / 2

        if device_params['num_pins_y'] == 0:
            bounding_box['left'] = body_edge['left']
            bounding_box['right'] = body_edge['right']
            if EP_size['x'] > dimensions['body_size_x'].nominal:
                bounding_box['left'] = -EP_size['x'] / 2
                bounding_box['right'] = EP_size['x'] / 2

        pad_width = pad_details['top']['size'][0]

        # ############################ SilkS ##################################
        silk_pad_offset = configuration[
            'silk_pad_clearance'] + configuration['silk_line_width'] / 2
        silk_offset = configuration['silk_fab_offset']

        right_pads_silk_bottom = (device_params['num_pins_y']-1)*device_params['pitch']/2\
            +pad_details['right']['size'][1]/2+silk_pad_offset
        silk_bottom = body_edge['bottom'] + silk_offset
        if EP_size['y'] / 2 <= body_edge[
                'bottom'] and right_pads_silk_bottom >= silk_bottom:
            silk_bottom = max(silk_bottom, EP_size['y'] / 2 + silk_pad_offset)

        silk_bottom = max(silk_bottom, right_pads_silk_bottom)
        silk_bottom = min(body_edge['bottom'] + silk_pad_offset, silk_bottom)

        bottom_pads_silk_right = (device_params['num_pins_x']-1)*device_params['pitch']/2\
            +pad_details['bottom']['size'][0]/2+silk_pad_offset
        silk_right = body_edge['right'] + silk_offset
        if EP_size['x'] / 2 <= body_edge[
                'right'] and bottom_pads_silk_right >= silk_right:
            silk_right = max(silk_right, EP_size['x'] / 2 + silk_pad_offset)
        silk_right = max(silk_right, bottom_pads_silk_right)
        silk_right = min(body_edge['right'] + silk_pad_offset, silk_right)

        min_lenght = configuration.get('silk_line_lenght_min', 0)
        silk_corner_bottom_right = Vector2D(silk_right, silk_bottom)

        silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine(
            pad_size=EP_size,
            pad_position=[0, 0],
            pad_radius=EP_round_radius,
            fixed_point=silk_corner_bottom_right,
            moving_point=Vector2D(0, silk_bottom),
            silk_pad_offset=silk_pad_offset,
            min_lenght=min_lenght)

        if silk_point_bottom_inside is not None and device_params[
                'num_pins_x'] > 0:
            silk_point_bottom_inside = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['bottom']['size'],
                pad_position=[
                    pad_details['bottom']['center'][0] +
                    (device_params['num_pins_x'] - 1) / 2 * pitch,
                    pad_details['bottom']['center'][1]
                ],
                pad_radius=pad_radius,
                fixed_point=silk_corner_bottom_right,
                moving_point=silk_point_bottom_inside,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        silk_point_right_inside = nearestSilkPointOnOrthogonalLine(
            pad_size=EP_size,
            pad_position=[0, 0],
            pad_radius=EP_round_radius,
            fixed_point=silk_corner_bottom_right,
            moving_point=Vector2D(silk_right, 0),
            silk_pad_offset=silk_pad_offset,
            min_lenght=min_lenght)
        if silk_point_right_inside is not None and device_params[
                'num_pins_y'] > 0:
            silk_point_right_inside = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['right']['size'],
                pad_position=[
                    pad_details['right']['center'][0],
                    pad_details['right']['center'][1] +
                    (device_params['num_pins_y'] - 1) / 2 * pitch
                ],
                pad_radius=pad_radius,
                fixed_point=silk_corner_bottom_right,
                moving_point=silk_point_right_inside,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        if silk_point_bottom_inside is None and silk_point_right_inside is not None:
            silk_corner_bottom_right['y'] = body_edge['bottom']
            silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['bottom']['size'],
                pad_position=[
                    pad_details['bottom']['center'][0] +
                    (device_params['num_pins_x'] - 1) / 2 * pitch,
                    pad_details['bottom']['center'][1]
                ],
                pad_radius=pad_radius,
                fixed_point=silk_point_right_inside,
                moving_point=silk_corner_bottom_right,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        elif silk_point_right_inside is None and silk_point_bottom_inside is not None:
            silk_corner_bottom_right['x'] = body_edge['right']
            silk_corner_bottom_right = nearestSilkPointOnOrthogonalLine(
                pad_size=pad_details['right']['size'],
                pad_position=[
                    pad_details['right']['center'][0],
                    pad_details['right']['center'][1] +
                    (device_params['num_pins_y'] - 1) / 2 * pitch
                ],
                pad_radius=pad_radius,
                fixed_point=silk_point_bottom_inside,
                moving_point=silk_corner_bottom_right,
                silk_pad_offset=silk_pad_offset,
                min_lenght=min_lenght)

        kicad_mod.append(
            Line(start=silk_point_bottom_inside,
                 end=silk_point_bottom_inside,
                 width=configuration['silk_line_width'],
                 layer="B.Fab"))

        poly_bottom_right = []
        if silk_point_bottom_inside is not None:
            poly_bottom_right.append(silk_point_bottom_inside)
        poly_bottom_right.append(silk_corner_bottom_right)
        if silk_point_right_inside is not None:
            poly_bottom_right.append(silk_point_right_inside)

        if len(poly_bottom_right) > 1 and silk_corner_bottom_right is not None:
            kicad_mod.append(
                PolygoneLine(polygone=poly_bottom_right,
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))
            kicad_mod.append(
                PolygoneLine(polygone=poly_bottom_right,
                             width=configuration['silk_line_width'],
                             layer="F.SilkS",
                             x_mirror=0))
            kicad_mod.append(
                PolygoneLine(polygone=poly_bottom_right,
                             width=configuration['silk_line_width'],
                             layer="F.SilkS",
                             y_mirror=0))

            if device_params['num_pins_y'] > 0:
                if silk_corner_bottom_right[
                        'y'] - min_lenght >= right_pads_silk_bottom:
                    poly_bottom_right[-1]['y'] = right_pads_silk_bottom
                    kicad_mod.append(
                        PolygoneLine(polygone=poly_bottom_right,
                                     width=configuration['silk_line_width'],
                                     layer="F.SilkS",
                                     y_mirror=0,
                                     x_mirror=0))
                    kicad_mod.append(
                        Line(start={
                            'x': -silk_corner_bottom_right['x'],
                            'y': -right_pads_silk_bottom
                        },
                             end={
                                 'x': bounding_box['left'],
                                 'y': -right_pads_silk_bottom
                             },
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))
                elif silk_corner_bottom_right[
                        'y'] >= right_pads_silk_bottom and silk_point_bottom_inside is not None:
                    kicad_mod.append(
                        Line(start=-silk_point_bottom_inside,
                             end={
                                 'x': bounding_box['left'],
                                 'y': -silk_point_bottom_inside['y']
                             },
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))
            else:
                if silk_corner_bottom_right[
                        'x'] - min_lenght >= bottom_pads_silk_right:
                    poly_bottom_right[0]['x'] = bottom_pads_silk_right
                    kicad_mod.append(
                        PolygoneLine(polygone=poly_bottom_right,
                                     width=configuration['silk_line_width'],
                                     layer="F.SilkS",
                                     y_mirror=0,
                                     x_mirror=0))
                    kicad_mod.append(
                        Line(start={
                            'x': -bottom_pads_silk_right,
                            'y': -silk_corner_bottom_right['y']
                        },
                             end={
                                 'x': -bottom_pads_silk_right,
                                 'y': bounding_box['top']
                             },
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))
                elif silk_corner_bottom_right[
                        'x'] >= bottom_pads_silk_right and silk_point_right_inside is not None:
                    kicad_mod.append(
                        Line(start=-silk_point_right_inside,
                             end={
                                 'x': -silk_point_right_inside['x'],
                                 'y': bounding_box['top']
                             },
                             width=configuration['silk_line_width'],
                             layer="F.SilkS"))

        # # ######################## Fabrication Layer ###########################

        fab_bevel_size = min(
            configuration['fab_bevel_size_absolute'],
            configuration['fab_bevel_size_relative'] * min(size_x, size_y))

        poly_fab = [
            {
                'x': body_edge['left'] + fab_bevel_size,
                'y': body_edge['top']
            },
            {
                'x': body_edge['right'],
                'y': body_edge['top']
            },
            {
                'x': body_edge['right'],
                'y': body_edge['bottom']
            },
            {
                'x': body_edge['left'],
                'y': body_edge['bottom']
            },
            {
                'x': body_edge['left'],
                'y': body_edge['top'] + fab_bevel_size
            },
            {
                'x': body_edge['left'] + fab_bevel_size,
                'y': body_edge['top']
            },
        ]

        kicad_mod.append(
            PolygoneLine(polygone=poly_fab,
                         width=configuration['fab_line_width'],
                         layer="F.Fab"))

        # # ############################ CrtYd ##################################

        off = ipc_data_set['courtyard']
        grid = configuration['courtyard_grid']

        if device_params['num_pins_y'] == 0 or device_params['num_pins_x'] == 0:
            cy1 = roundToBase(bounding_box['top'] - off, grid)

            kicad_mod.append(
                RectLine(start={
                    'x': roundToBase(bounding_box['left'] - off, grid),
                    'y': cy1
                },
                         end={
                             'x': roundToBase(bounding_box['right'] + off,
                                              grid),
                             'y': roundToBase(bounding_box['bottom'] + off,
                                              grid)
                         },
                         width=configuration['courtyard_line_width'],
                         layer='F.CrtYd'))

        else:
            cy1 = roundToBase(bounding_box['top'] - off, grid)
            cy2 = roundToBase(body_edge['top'] - off, grid)
            cy3 = -roundToBase(
                device_params['pitch'] *
                (device_params['num_pins_y'] - 1) / 2.0 + pad_width / 2.0 +
                off, grid)

            cx1 = -roundToBase(
                device_params['pitch'] *
                (device_params['num_pins_x'] - 1) / 2.0 + pad_width / 2.0 +
                off, grid)
            cx2 = roundToBase(body_edge['left'] - off, grid)
            cx3 = roundToBase(bounding_box['left'] - off, grid)

            crty_poly_tl = [{
                'x': 0,
                'y': cy1
            }, {
                'x': cx1,
                'y': cy1
            }, {
                'x': cx1,
                'y': cy2
            }, {
                'x': cx2,
                'y': cy2
            }, {
                'x': cx2,
                'y': cy3
            }, {
                'x': cx3,
                'y': cy3
            }, {
                'x': cx3,
                'y': 0
            }]
            kicad_mod.append(
                PolygoneLine(polygone=crty_poly_tl,
                             layer='F.CrtYd',
                             width=configuration['courtyard_line_width']))
            kicad_mod.append(
                PolygoneLine(polygone=crty_poly_tl,
                             layer='F.CrtYd',
                             width=configuration['courtyard_line_width'],
                             x_mirror=0))
            kicad_mod.append(
                PolygoneLine(polygone=crty_poly_tl,
                             layer='F.CrtYd',
                             width=configuration['courtyard_line_width'],
                             y_mirror=0))
            kicad_mod.append(
                PolygoneLine(polygone=crty_poly_tl,
                             layer='F.CrtYd',
                             width=configuration['courtyard_line_width'],
                             x_mirror=0,
                             y_mirror=0))

        # ######################### Text Fields ###############################

        addTextFields(kicad_mod=kicad_mod,
                      configuration=configuration,
                      body_edges=body_edge,
                      courtyard={
                          'top': cy1,
                          'bottom': -cy1
                      },
                      fp_name=fp_name,
                      text_y_inside_position='center')

        ##################### Output and 3d model ############################

        kicad_mod.append(Model(filename=model_name))

        output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
        if not os.path.isdir(
                output_dir
        ):  #returns false if path does not yet exist!! (Does not check path validity)
            os.makedirs(output_dir)
        filename = '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir,
                                                            fp_name=fp_name)

        file_handler = KicadFileHandler(kicad_mod)
        file_handler.writeFile(filename)
    def __createFootprintVariant(self, device_params, device_dimensions, with_thermal_vias):
        fab_line_width = self.configuration.get('fab_line_width', 0.1)
        silk_line_width = self.configuration.get('silk_line_width', 0.12)

        lib_name = device_params.get('library', default_library)

        pincount = device_params['num_pins_x']*2 + device_params['num_pins_y']*2

        default_ipc_config = 'qfn_pull_back' if 'lead_to_edge' in device_params else 'qfn'
        if device_params.get('ipc_class', default_ipc_config) == 'qfn_pull_back':
            ipc_reference = 'ipc_spec_flat_no_lead_pull_back'
        else:
            ipc_reference = 'ipc_spec_flat_no_lead'

        used_density = device_params.get('ipc_density', ipc_density)
        ipc_data_set = self.ipc_defintions[ipc_reference][used_density]
        ipc_round_base = self.ipc_defintions[ipc_reference]['round_base']

        layout = ''
        if device_dimensions['has_EP']:
            name_format = self.configuration['fp_name_EP_format_string_no_trailing_zero']
            if 'EP_size_x_overwrite' in device_params:
                EP_size = {
                    'x':device_params['EP_size_x_overwrite'],
                    'y':device_params['EP_size_y_overwrite']
                    }
            else:
                EP_size = {
                    'x':device_dimensions['EP_size_x'].nominal,
                    'y':device_dimensions['EP_size_y'].nominal
                    }
            EP_center = {
                'x':device_dimensions['EP_center_x'].nominal,
                'y':device_dimensions['EP_center_y'].nominal
                }
        else:
            name_format = self.configuration['fp_name_format_string_no_trailing_zero']
            if device_params.get('use_name_format', 'QFN') == 'LGA':
                name_format = self.configuration['fp_name_lga_format_string_no_trailing_zero']
                if device_params['num_pins_x'] > 0 and device_params['num_pins_y'] > 0:
                    layout = self.configuration['lga_layout_border'].format(
                        nx=device_params['num_pins_x'], ny=device_params['num_pins_y'])

            EP_size = {'x':0, 'y':0}

        if 'custom_name_format' in device_params:
            name_format = device_params['custom_name_format']

        pad_details = self.calcPadDetails(device_dimensions, EP_size, ipc_data_set, ipc_round_base)


        pad_suffix = '_Pad{pad_x:.2f}x{pad_y:.2f}mm'.format(pad_x=pad_details['left']['size'][0],
            pad_y=pad_details['left']['size'][1])
        pad_suffix = '' if device_params.get('include_pad_size', 'none') not in ('fp_name_only', 'both') else pad_suffix
        pad_suffix_3d = '' if device_params.get('include_pad_size', 'none') not in ('both') else pad_suffix

        suffix = device_params.get('suffix', '')
        suffix_3d = suffix if device_params.get('include_suffix_in_3dpath', 'True') == 'True' else ""

        model3d_path_prefix = self.configuration.get('3d_model_prefix','${KISYS3DMOD}')

        size_x = device_dimensions['body_size_x'].nominal
        size_y = device_dimensions['body_size_y'].nominal

        fp_name = name_format.format(
            man=device_params.get('manufacturer',''),
            mpn=device_params.get('part_number',''),
            pkg=device_params['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            layout=layout,
            ep_size_x = EP_size['x'],
            ep_size_y = EP_size['y'],
            suffix=pad_suffix,
            suffix2=suffix,
            vias=self.configuration.get('thermal_via_suffix', '_ThermalVias') if with_thermal_vias else ''
            ).replace('__','_').lstrip('_')

        fp_name_2 = name_format.format(
            man=device_params.get('manufacturer',''),
            mpn=device_params.get('part_number',''),
            pkg=device_params['device_type'],
            pincount=pincount,
            size_y=size_y,
            size_x=size_x,
            pitch=device_params['pitch'],
            layout=layout,
            ep_size_x = EP_size['x'],
            ep_size_y = EP_size['y'],
            suffix=pad_suffix_3d,
            suffix2=suffix_3d,
            vias=''
            ).replace('__','_').lstrip('_')

        if 'fp_name_prefix' in device_params:
            prefix = device_params['fp_name_prefix']
            if not prefix.endswith('_'):
                prefix += '_'
            fp_name = prefix + fp_name
            fp_name_2 = prefix + fp_name_2

        model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'\
            .format(
                model3d_path_prefix=model3d_path_prefix, lib_name=lib_name,
                fp_name=fp_name_2)
        #print(fp_name)
        #print(pad_details)

        kicad_mod = Footprint(fp_name)

                # init kicad footprint
        kicad_mod.setDescription(
            "{manufacturer} {mpn} {package}, {pincount} Pin ({datasheet}), generated with kicad-footprint-generator {scriptname}"\
            .format(
                manufacturer = device_params.get('manufacturer',''),
                package = device_params['device_type'],
                mpn = device_params.get('part_number',''),
                pincount = pincount,
                datasheet = device_params['size_source'],
                scriptname = os.path.basename(__file__).replace("  ", " ")
                ).lstrip())

        kicad_mod.setTags(self.configuration['keyword_fp_string']\
            .format(
                man=device_params.get('manufacturer',''),
                package=device_params['device_type'],
                category=category
            ).lstrip())
        kicad_mod.setAttribute('smd')

        pad_shape_details = {}
        pad_shape_details['shape'] = Pad.SHAPE_ROUNDRECT
        pad_shape_details['radius_ratio'] = configuration.get('round_rect_radius_ratio', 0)
        if 'round_rect_max_radius' in configuration:
            pad_shape_details['maximum_radius'] = configuration['round_rect_max_radius']

        if device_dimensions['has_EP']:
            if with_thermal_vias:
                thermals = device_params['thermal_vias']
                paste_coverage = thermals.get('EP_paste_coverage',
                                               device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE))

                kicad_mod.append(ExposedPad(
                    number=pincount+1, size=EP_size,
                    at=EP_center,
                    paste_layout=thermals.get('EP_num_paste_pads', device_params.get('EP_num_paste_pads', 1)),
                    paste_coverage=paste_coverage,
                    via_layout=thermals.get('count', 0),
                    paste_between_vias=thermals.get('paste_between_vias'),
                    paste_rings_outside=thermals.get('paste_rings_outside'),
                    via_drill=thermals.get('drill', 0.3),
                    via_grid=thermals.get('grid'),
                    paste_avoid_via=thermals.get('paste_avoid_via', True),
                    via_paste_clarance=thermals.get('paste_via_clearance', DEFAULT_VIA_PASTE_CLEARANCE),
                    min_annular_ring=thermals.get('min_annular_ring', DEFAULT_MIN_ANNULAR_RING),
                    bottom_pad_min_size=thermals.get('bottom_min_size', 0),
                    kicad4_compatible=args.kicad4_compatible,
                    **pad_shape_details
                    ))
            else:
                kicad_mod.append(ExposedPad(
                    number=pincount+1, size=EP_size,
                    at=EP_center,
                    paste_layout=device_params.get('EP_num_paste_pads', 1),
                    paste_coverage=device_params.get('EP_paste_coverage', DEFAULT_PASTE_COVERAGE),
                    kicad4_compatible=args.kicad4_compatible,
                    **pad_shape_details
                    ))

        add_dual_or_quad_pad_border(kicad_mod, configuration, pad_details, device_params)

        body_edge = {
            'left': -size_x/2,
            'right': size_x/2,
            'top': -size_y/2,
            'bottom': size_y/2
            }

        bounding_box = body_edge.copy()

        if device_params['num_pins_x'] == 0 and EP_size['y'] > size_y:
                bounding_box['top'] = -EP_size['y']/2
                bounding_box['bottom'] = EP_size['y']/2

        if device_params['num_pins_y'] == 0 and EP_size['x'] > size_x:
                bounding_box['left'] = -EP_size['x']/2
                bounding_box['right'] = EP_size['x']/2

        if device_params['num_pins_y'] > 0:
            bounding_box['left'] = pad_details['left']['center'][0] - pad_details['left']['size'][0]/2

            bounding_box['right'] = pad_details['right']['center'][0] + pad_details['right']['size'][0]/2

        if device_params['num_pins_x'] > 0:
            bounding_box['top'] = pad_details['top']['center'][1] - pad_details['top']['size'][1]/2

            bounding_box['bottom'] = pad_details['bottom']['center'][1] + pad_details['bottom']['size'][1]/2

        pad_width = pad_details['top']['size'][0]

        # ############################ SilkS ##################################

        silk_pad_offset = configuration['silk_pad_clearance'] + configuration['silk_line_width']/2
        silk_offset = configuration['silk_fab_offset']
        if device_params['num_pins_x'] == 0:
            kicad_mod.append(Line(
                start={'x':0,
                    'y':body_edge['top']-silk_offset},
                end={'x':body_edge['right'],
                    'y':body_edge['top']-silk_offset},
                width=configuration['silk_line_width'],
                layer="F.SilkS"))
            kicad_mod.append(Line(
                start={'x':body_edge['left'],
                    'y':body_edge['bottom']+silk_offset},
                end={'x':body_edge['right'],
                    'y':body_edge['bottom']+silk_offset},
                width=configuration['silk_line_width'],
                layer="F.SilkS", y_mirror=0))
        elif device_params['num_pins_y'] == 0:
            kicad_mod.append(Line(
                start={'y':0,
                    'x':body_edge['left']-silk_offset},
                end={'y':body_edge['bottom'],
                    'x':body_edge['left']-silk_offset},
                width=configuration['silk_line_width'],
                layer="F.SilkS"))
            kicad_mod.append(Line(
                start={'y':body_edge['top'],
                    'x':body_edge['right']+silk_offset},
                end={'y':body_edge['bottom'],
                    'x':body_edge['right']+silk_offset},
                width=configuration['silk_line_width'],
                layer="F.SilkS", x_mirror=0))
        else:
            sx1 = -(device_params['pitch']*(device_params['num_pins_x']-1)/2.0
                + pad_width/2.0 + silk_pad_offset)

            sy1 = -(device_params['pitch']*(device_params['num_pins_y']-1)/2.0
                + pad_width/2.0 + silk_pad_offset)

            poly_silk = [
                {'x': sx1, 'y': body_edge['top']-silk_offset},
                {'x': body_edge['left']-silk_offset, 'y': body_edge['top']-silk_offset},
                {'x': body_edge['left']-silk_offset, 'y': sy1}
            ]
            if sx1 - SILK_MIN_LEN < body_edge['left']-silk_offset:
                poly_silk = poly_silk[1:]
            if sy1 - SILK_MIN_LEN < body_edge['top']-silk_offset:
                poly_silk = poly_silk[:-1]
            if len(poly_silk) > 1:
                kicad_mod.append(PolygoneLine(
                    polygone=poly_silk,
                    width=configuration['silk_line_width'],
                    layer="F.SilkS", x_mirror=0))
                kicad_mod.append(PolygoneLine(
                    polygone=poly_silk,
                    width=configuration['silk_line_width'],
                    layer="F.SilkS", y_mirror=0))
                kicad_mod.append(PolygoneLine(
                    polygone=poly_silk,
                    width=configuration['silk_line_width'],
                    layer="F.SilkS", x_mirror=0, y_mirror=0))
                if len(poly_silk) > 2:
                    kicad_mod.append(Line(
                        start={'x': sx1, 'y': body_edge['top']-silk_offset},
                        end={'x': body_edge['left']-silk_offset, 'y': body_edge['top']-silk_offset},
                        width=configuration['silk_line_width'],
                        layer="F.SilkS"))

        # # ######################## Fabrication Layer ###########################

        fab_bevel_size = min(configuration['fab_bevel_size_absolute'], configuration['fab_bevel_size_relative']*min(size_x, size_y))

        poly_fab = [
            {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']},
            {'x': body_edge['right'], 'y': body_edge['top']},
            {'x': body_edge['right'], 'y': body_edge['bottom']},
            {'x': body_edge['left'], 'y': body_edge['bottom']},
            {'x': body_edge['left'], 'y': body_edge['top']+fab_bevel_size},
            {'x': body_edge['left']+fab_bevel_size, 'y': body_edge['top']},
        ]

        kicad_mod.append(PolygoneLine(
            polygone=poly_fab,
            width=configuration['fab_line_width'],
            layer="F.Fab"))

        # # ############################ CrtYd ##################################

        off = ipc_data_set['courtyard']
        grid = configuration['courtyard_grid']

        cy1=roundToBase(bounding_box['top']-off, grid)

        kicad_mod.append(RectLine(
            start={
                'x':roundToBase(bounding_box['left']-off, grid),
                'y':cy1
                },
            end={
                'x':roundToBase(bounding_box['right']+off, grid),
                'y':roundToBase(bounding_box['bottom']+off, grid)
                },
            width=configuration['courtyard_line_width'],
            layer='F.CrtYd'))

        # ######################### Text Fields ###############################

        addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=body_edge,
            courtyard={'top': cy1, 'bottom': -cy1}, fp_name=fp_name, text_y_inside_position='center')

        ##################### Output and 3d model ############################

        kicad_mod.append(Model(filename=model_name))

        output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
        if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity)
            os.makedirs(output_dir)
        filename =  '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=fp_name)

        file_handler = KicadFileHandler(kicad_mod)
        file_handler.writeFile(filename)