Example #1
0
    def __init__(self, shape):

        gerber_lp = None
        mirror = False

        # Invert rotation so it's clock-wise. Inkscape is counter-clockwise and
        # it's unclear to ma what's the "right" direction. clockwise makse more
        # sense to me. This should be the only place to make the change.
        self._inv_rotate = -1

        self._rotate = shape.get('rotate') or 0
        self._rotate *= self._inv_rotate
        self._rotate_point = shape.get('rotate-point') or Point(0,0)
        self._scale = shape.get('scale') or 1
        self._pour_buffer = shape.get('buffer-to-pour')

        try:
            self._type = shape.get('type')
        except:
            msg.error("Shapes must have a 'type' defined")

        if self._type in ['rect', 'rectangle']:
            path = svg.width_and_height_to_path(shape['width'], shape['height'], shape.get('radii'))
        elif self._type in ['circ', 'circle', 'round']:
            path = svg.circle_diameter_to_path(shape['diameter'])
        elif self._type in ['drill']:
            self._diameter = shape['diameter']
            path = svg.drillPath(self._diameter)
        elif self._type in ['text', 'string']:
            try:
                self._text = shape['value']
            except KeyError:
                msg.error("Could not find the text to display. The text to be displayed should be defined in the 'value' field, for example, 'value': 'DEADBEEF\\nhar\\nhar'")

            # Get the fon'ts name
            font = shape.get('font-family') or config.stl['layout']['defaults']['font-family']

            # Search for the font SVG in these paths
            paths = [os.path.join(config.cfg['base-dir']),
                     os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')]

            font_filename = "%s.svg" % font
            filenames = ''
            font_data = None
            for path in paths:
                filename = os.path.join(path,
                                        config.cfg['locations']['fonts'],
                                        font_filename)
                filenames += "  %s \n" % filename
                if os.path.isfile(filename):
                    font_data = et.ElementTree(file=filename)
                    break

            if font_data == None:
                msg.error("Couldn't find style file %s. Looked for it here:\n%s" % (font_filename, filenames))

            try:
                fs = shape['font-size']
            except:
                msg.error("A 'font-size' attribute must be specified for a 'text' type")

            ls = shape.get('letter-spacing') or '0mm'
            lh = shape.get('line-height') or fs

            font_size, letter_spacing, line_height = utils.getTextParams(fs,
                                                                         ls, 
                                                                         lh)

            # With the units-per-em we can figure out the scale factor
            # to use for the desired font size
            units_per_em = float(font_data.find("//n:font-face", namespaces={'n': config.cfg['namespace']['svg']}).get('units-per-em')) or 1000
            self._scale = font_size/units_per_em

            # Get the path to use. This returns the path without
            # scaling, which will be applied later, in the same manner
            # as to the other shape types
            path, gerber_lp = utils.textToPath(font_data,
                                               self._text,
                                               letter_spacing,
                                               line_height,
                                               self._scale)
           
            # In the case where the text is an outline/stroke instead
            # of a fill we get rid of the gerber_lp
            if shape.get('style') == 'stroke':
                gerber_lp = None

            self._rotate += 180

        elif self._type in ['path']:
            path = shape.get('value')
        else:
            msg.error("'%s' is not a recongnised shape type" % self._type)


        self._path = SvgPath(path, gerber_lp)
        self._path.transform(self._scale, self._rotate, self._rotate_point, True)

        self._gerber_lp = (shape.get('gerber-lp') or 
                           shape.get('gerber_lp') or 
                           gerber_lp or 
                           None)

        self._location = utils.toPoint(shape.get('location', [0, 0]))
Example #2
0
def placeDrill(drill,
               layer,
               location,
               scale,
               soldermask_layers={},
               mask_groups={}):
    """
    Places the drilling point
    """

    diameter = drill.get('diameter')
    offset = utils.to_Point(drill.get('offset') or [0, 0])
    path = svg.drill_diameter_to_path(diameter)
    mask_path = svg.circle_diameter_to_path(diameter)

    sig_dig = config.cfg['significant-digits']
    transform = 'translate(%s %s)' % (round(
        (location.x + offset.x) * scale,
        sig_dig), round((-location.y - offset.y) * scale, sig_dig))

    drill_element = et.SubElement(layer,
                                  'path',
                                  transform=transform,
                                  d=path,
                                  id='pad_drill',
                                  diameter=str(diameter))

    pour_buffer = 1.0
    try:
        pour_buffer = board_cfg['distances']['buffer_from_pour_to'].get(
            'drill') or 1.0
    except:
        pass

    # add a mask buffer between pour and board outline
    if mask_groups != {}:
        for pcb_layer in surface_layers:
            mask_group = et.SubElement(mask_groups[pcb_layer],
                                       'g',
                                       id="drill_masks")
            pour_mask = et.SubElement(mask_group,
                                      'path',
                                      transform=transform,
                                      style=MASK_STYLE % str(pour_buffer * 2),
                                      gerber_lp="c",
                                      d=mask_path)

    # place the size of the drill; id the drill element has a
    # "show_diameter": "no", then this can be suppressed
    # default to 'yes'
    show_diameter = drill.get('show_diameter') or 'yes'
    if show_diameter.lower() != 'no':
        text = "%s mm" % (str(diameter))
        text_style = config.stl['layout']['drills'].get('text') or None
        if text_style is not None:
            text_style['font-size'] = str(diameter / 10.0) + 'px'
            text_style = utils.dict_to_style(text_style)
            t = et.SubElement(
                layer,
                'text',
                x=str(location.x),
                # TODO: get rid of this hack
                y=str(-location.y - (diameter / 4)),
                style=text_style)
            t.text = text

    # place soldermask unless specified otherwise
    # default is 'yes'
    add_soldermask = drill.get('add_soldermask') or 'yes'
    style = utils.dict_to_style(config.stl['layout']['soldermask'].get('fill'))
    possible_answers = [
        'yes', 'top', 'top only', 'bottom', 'bottom only', 'top and bottom'
    ]
    if (add_soldermask.lower()
            in possible_answers) and (soldermask_layers != {}):
        # TODO: get this into a configuration parameter
        drill_soldermask_scale_factors = drill.get(
            'soldermask_scale_factors') or {
                'top': 1.2,
                'bottom': 1.2
            }
        path_top = svg.circle_diameter_to_path(
            diameter * drill_soldermask_scale_factors['top'])
        path_bottom = svg.circle_diameter_to_path(
            diameter * drill_soldermask_scale_factors['bottom'])

        if add_soldermask.lower() == 'yes' or add_soldermask.lower(
        ) == 'top and bottom':
            drill_element = et.SubElement(soldermask_layers['top'],
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_top)
            drill_element = et.SubElement(soldermask_layers['bottom'],
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_bottom)
        elif add_soldermask.lower() == 'top only' or add_soldermask.lower(
        ) == 'top':
            drill_element = et.SubElement(soldermask_layers['top'],
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_top)
        elif add_soldermask.lower() == 'bottom only' or add_soldermask.lower(
        ) == 'bottom':
            drill_element = et.SubElement(soldermask_layers['bottom'],
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_bottom)
        else:
            print "ERROR: unrecognised drills soldermask option"

    return
Example #3
0
    def __init__(self, shape):

        gerber_lp = None
        mirror = False

        self._shape_dict = shape

        # Invert rotation so it's clock-wise. Inkscape is counter-clockwise and
        # it's unclear to ma what's the "right" direction. clockwise makse more
        # sense to me. This should be the only place to make the change.
        self._inv_rotate = -1

        try:
            self._type = shape.get('type')
        except:
            msg.error("Shapes must have a 'type' defined")

        # A 'layer' type is a copy of the outline. Here we copy the
        # outline shape and override the type
        if self._type in ['layer']:
            self._shape_dict = config.brd['outline'].get('shape').copy()
            self._type = self._shape_dict.get('type')

        self._place_mirrored = shape.get('mirror') or False

        self._rotate = shape.get('rotate') or 0
        self._rotate *= self._inv_rotate
        self._rotate_point = shape.get('rotate-point') or Point(0, 0)
        self._scale = shape.get('scale') or 1
        self._pour_buffer = shape.get('buffer-to-pour')

        # A general purpose label field; intended for use for pad
        # labels
        self._label = None

        if self._type in ['rect', 'rectangle']:
            path = svg.width_and_height_to_path(self._shape_dict['width'],
                                                self._shape_dict['height'],
                                                self._shape_dict.get('radii'))
        elif self._type in ['circ', 'circle', 'round']:
            path = svg.circle_diameter_to_path(self._shape_dict['diameter'])
        elif self._type in ['drill']:
            self._diameter = self._shape_dict['diameter']
            path = svg.drillPath(self._diameter)
        elif self._type in ['text', 'string']:
            try:
                self._text = self._shape_dict['value']
            except KeyError:
                msg.error(
                    "Could not find the text to display. The text to be displayed should be defined in the 'value' field, for example, 'value': 'DEADBEEF\\nhar\\nhar'"
                )

            # Get the font's name
            font = self._shape_dict.get('font-family') or config.stl['layout'][
                'defaults']['font-family']

            # Search for the font SVG in these paths
            paths = [
                os.path.join(config.cfg['base-dir']),
                os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
            ]

            font_filename = "%s.svg" % font
            filenames = ''
            font_data = None
            for path in paths:
                filename = os.path.join(path, config.cfg['locations']['fonts'],
                                        font_filename)
                filenames += "  %s \n" % filename
                if os.path.isfile(filename):
                    font_data = et.ElementTree(file=filename)
                    break

            if font_data == None:
                msg.error(
                    "Couldn't find style file %s. Looked for it here:\n%s" %
                    (font_filename, filenames))

            try:
                fs = self._shape_dict['font-size']
            except:
                msg.error(
                    "A 'font-size' attribute must be specified for a 'text' type"
                )

            ls = self._shape_dict.get('letter-spacing') or '0mm'
            lh = self._shape_dict.get('line-height') or fs

            font_size, letter_spacing, line_height = utils.getTextParams(
                fs, ls, lh)

            # With the units-per-em we can figure out the scale factor
            # to use for the desired font size
            units_per_em = float(
                font_data.find("//n:font-face",
                               namespaces={
                                   'n': config.cfg['namespace']['svg']
                               }).get('units-per-em')) or 1000
            self._scale = font_size / units_per_em

            # Get the path to use. This returns the path without
            # scaling, which will be applied later, in the same manner
            # as to the other shape types
            path, gerber_lp = utils.textToPath(font_data, self._text,
                                               letter_spacing, line_height,
                                               self._scale)

            # In the case where the text is an outline/stroke instead
            # of a fill we get rid of the gerber_lp
            if self._shape_dict.get('style') == 'stroke':
                gerber_lp = None

            self._rotate += 180

        elif self._type in ['path']:
            path = self._shape_dict.get('value')
        else:
            msg.error("'%s' is not a recongnised shape type" % self._type)

        self._path = SvgPath(path, gerber_lp)

        self._path.transform(scale=self._scale,
                             rotate_angle=self._rotate,
                             rotate_point=self._rotate_point,
                             mirror=self._place_mirrored)

        self._gerber_lp = (shape.get('gerber-lp') or shape.get('gerber_lp')
                           or gerber_lp or None)

        self._location = utils.toPoint(shape.get('location', [0, 0]))
Example #4
0
def placeDrill(drill, 
               layer, 
               location, 
               scale, 
               soldermask_layers={}, 
               mask_groups={}):
    """
    Places the drilling point
    """

    diameter = drill.get('diameter')
    offset = utils.to_Point(drill.get('offset') or [0, 0]) 
    path = svg.drill_diameter_to_path(diameter)
    mask_path = svg.circle_diameter_to_path(diameter)

    sig_dig = config.cfg['significant-digits']
    #translate = str(round((location.x + offset.x)*scale, sig_dig))+' '+str(round((-location.y - offset.y)*scale, sig_dig))
    transform = 'translate(%s %s)' % (round((location.x + offset.x)*scale, sig_dig),
                                      round((-location.y - offset.y)*scale, sig_dig))

    drill_element = et.SubElement(layer, 'path',
                                  transform=transform,
                                  d=path,
                                  id='pad_drill',
                                  diameter=str(diameter))

    pour_buffer = 1.0
    try:
        pour_buffer = board_cfg['distances']['buffer_from_pour_to'].get('drill') or 1.0
    except:
        pass

    # add a mask buffer between pour and board outline
    if mask_groups != {}:
        for pcb_layer in surface_layers:
            mask_group = et.SubElement(mask_groups[pcb_layer], 'g',
                                            id="drill_masks")
            pour_mask = et.SubElement(mask_group, 'path',
                                      transform=transform,
                                      style=MASK_STYLE % str(pour_buffer*2),
                                      gerber_lp="c",
                                      d=mask_path)



    # place the size of the drill; id the drill element has a
    # "show_diameter": "no", then this can be suppressed
    # default to 'yes'
    show_diameter = drill.get('show_diameter') or 'yes'
    if show_diameter.lower() != 'no':
        text = "%s mm" % (str(diameter))
        text_style = config.stl['layout']['drills'].get('text') or None
        if text_style is not None:
            text_style['font-size'] = str(diameter/10.0)+'px'
            text_style = utils.dict_to_style(text_style)
            t = et.SubElement(layer, 'text',
                x=str(location.x),
                # TODO: get rid of this hack
                y=str(-location.y-(diameter/4)),
                style=text_style)
            t.text = text

    # place soldermask unless specified otherwise
    # default is 'yes'
    add_soldermask = drill.get('add_soldermask') or 'yes'
    style = utils.dict_to_style(config.stl['layout']['soldermask'].get('fill'))
    possible_answers = ['yes', 'top', 'top only', 'bottom', 'bottom only', 'top and bottom']
    if (add_soldermask.lower() in possible_answers) and (soldermask_layers != {}):
        # TODO: get this into a configuration parameter
        drill_soldermask_scale_factors = drill.get('soldermask_scale_factors') or {'top':1.2, 'bottom':1.2}
        path_top = svg.circle_diameter_to_path(diameter * drill_soldermask_scale_factors['top'])
        path_bottom = svg.circle_diameter_to_path(diameter * drill_soldermask_scale_factors['bottom'])

        if add_soldermask.lower() == 'yes' or add_soldermask.lower() == 'top and bottom':
            drill_element = et.SubElement(soldermask_layers['top'], 
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_top)
            drill_element = et.SubElement(soldermask_layers['bottom'], 
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_bottom)
        elif add_soldermask.lower() == 'top only' or add_soldermask.lower() == 'top':
            drill_element = et.SubElement(soldermask_layers['top'], 
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_top)
        elif add_soldermask.lower() == 'bottom only' or add_soldermask.lower() == 'bottom':
            drill_element = et.SubElement(soldermask_layers['bottom'], 
                                          'path',
                                          transform=transform,
                                          style=style,
                                          d=path_bottom)
        else:
            print "ERROR: unrecognised drills soldermask option"

    return