Example #1
0
    def _placeOutline(self):
        """
        """
        # Place shape
        shape_group = et.SubElement(self._layers['outline']['layer'], 'g')
        shape_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'module-shapes')
        place.placeShape(self._outline, shape_group)

        # Place a mask for the board's outline. This creates a buffer between 
        # the board's edge and pours
        try:
            pour_buffer = self._module['distances']['from-pour-to']['outline']
        except:
            pour_buffer = config.brd['distances']['from-pour-to']['outline']

        for pcb_layer in utils.getSurfaceLayers():
            if utils.checkForPoursInLayer(pcb_layer) is True:
                mask_element = place.placeShape(self._outline, self._masks[pcb_layer])
                # Override style so that we get the desired effect
                # We stroke the outline with twice the size of the buffer, so
                # we get the actual distance between the outline and board
                style = "fill:none;stroke:#000;stroke-linejoin:round;stroke-width:%s;" % str(pour_buffer*2)
                mask_element.set('style', style)

                # Also override mask's gerber-lp and set to all clear
                path = self._outline.getOriginalPath().lower()
                segments = path.count('m')
                mask_element.set('{'+config.cfg['ns']['pcbmode']+'}gerber-lp', 'c'*segments)
Example #2
0
    def _placeOutline(self):
        """
        """
        # Place shape
        shape_group = et.SubElement(self._layers['outline']['layer'], 'g')
        shape_group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                        'module-shapes')
        place.placeShape(self._outline, shape_group)

        # Place a mask for the board's outline. This creates a buffer between
        # the board's edge and pours
        try:
            pour_buffer = self._module['distances']['from-pour-to']['outline']
        except:
            pour_buffer = config.brd['distances']['from-pour-to']['outline']

        for pcb_layer in utils.getSurfaceLayers():
            if utils.checkForPoursInLayer(pcb_layer) is True:
                mask_element = place.placeShape(self._outline,
                                                self._masks[pcb_layer])
                # Override style so that we get the desired effect
                # We stroke the outline with twice the size of the buffer, so
                # we get the actual distance between the outline and board
                style = "fill:none;stroke:#000;stroke-linejoin:round;stroke-width:%s;" % str(
                    pour_buffer * 2)
                mask_element.set('style', style)

                # Also override mask's gerber-lp and set to all clear
                path = self._outline.getOriginalPath().lower()
                segments = path.count('m')
                mask_element.set(
                    '{' + config.cfg['ns']['pcbmode'] + '}gerber-lp',
                    'c' * segments)
Example #3
0
    def _placeLayerIndex(self):
        """
        Adds a drill index
        """

        text_dict = config.stl['layout']['layer-index']['text']
        text_dict['type'] = 'text'

        # Set the height (and width) of the rectangle (square) to the
        # size of the text
        rect_width = utils.parseDimension(text_dict['font-size'])[0]
        rect_height = rect_width
        rect_gap = 0.25

        # Get location, or generate one
        try:
            location = config.brd['layer-index']['location']
        except:
            # If not location is specified, put the drill index at the
            # top right of the board. The 'gap' defines the extra
            # spcae between the top of the largest drill and the
            # board's edge
            gap = 2
            location = [self._width/2+gap, self._height/2-rect_height/2]
        location = utils.toPoint(location)        

        rect_dict = {}
        rect_dict['type'] = 'rect'
        rect_dict['style'] = 'fill'
        rect_dict['width'] = rect_width
        rect_dict['height'] = rect_height

        # Create group for placing index
        for pcb_layer in utils.getSurfaceLayers():
            for sheet in ['copper', 'soldermask', 'silkscreen', 'assembly', 'solderpaste']:
                layer = self._layers[pcb_layer][sheet]['layer']
                transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y)
                group = et.SubElement(layer, 'g',
                                      transform=transform)
                group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'layer-index')

                rect_shape = Shape(rect_dict)
                style = Style(rect_dict, sheet)
                rect_shape.setStyle(style)
                place.placeShape(rect_shape, group)

                text_dict['value'] = "%s %s" % (pcb_layer, sheet)
                #text_dict['location'] = [rect_width+rect_gap+text_width, 0]
                text_shape = Shape(text_dict)
                text_width = text_shape.getWidth()
                style = Style(text_dict, sheet)
                text_shape.setStyle(style)
                element = place.placeShape(text_shape, group)
                element.set("transform", "translate(%s,%s)" % (rect_width/2+rect_gap+text_width/2, 0))

                location.y += config.cfg['invert-y']*(rect_height+rect_gap)

            location.y += config.cfg['invert-y']*(rect_height+rect_gap*2)
Example #4
0
    def _placePours(self):
        """
        """

        try:
            pours = self._module_dict['shapes']['pours']
        except:
            return

        shape_group = {}
        for pcb_layer in utils.getSurfaceLayers():
            svg_layer = self._layers[pcb_layer]['copper']['pours']['layer']
            shape_group[pcb_layer] = et.SubElement(svg_layer,
                                                   'g',
                                                   mask='url(#mask-%s)' %
                                                   pcb_layer)

        for pour_dict in pours:
            try:
                pour_type = pour_dict['type']
            except:
                msg.error(
                    "Cannot find a 'type' for a pour shape. Pours can be any 'shape', or simply 'type':'layer' to cover the entire layer."
                )

            layers = pour_dict.get('layers') or ['top']

            if pour_type == 'layer':
                # Get the outline shape dict
                new_pour_dict = self._module_dict['outline'].get(
                    'shape').copy()
                new_pour_dict['style'] = 'fill'
                shape = Shape(new_pour_dict)
                # Get the appropriate style from copper->pours
                style = Style(new_pour_dict,
                              layer_name='copper',
                              sub_item='pours')
                shape.setStyle(style)
            else:
                shape = Shape(pour_dict)
                # Get the appropriate style from copper->pours
                style = Style(pour_dict, layer_name='copper', sub_item='pours')
                shape.setStyle(style)

            # Place on all specified layers
            for layer in layers:
                place.placeShape(shape, shape_group[layer])
Example #5
0
    def _placePours(self):
        """
        """

        try:
            pours = self._module_dict['shapes']['pours']
        except:
            return

        shape_group = {}
        for pcb_layer in utils.getSurfaceLayers():
            svg_layer = self._layers[pcb_layer]['copper']['pours']['layer']
            shape_group[pcb_layer] = et.SubElement(svg_layer, 'g',
                                                   mask='url(#mask-%s)' % pcb_layer)

        for pour_dict in pours:
            try:
                pour_type = pour_dict['type']
            except:
                msg.error("Cannot find a 'type' for a pour shape. Pours can be any 'shape', or simply 'type':'layer' to cover the entire layer.")

            layers = pour_dict.get('layers') or ['top']

            if pour_type == 'layer':
                # Get the outline shape dict
                new_pour_dict = self._module_dict['outline'].get('shape').copy()
                new_pour_dict['style'] = 'fill'
                shape = Shape(new_pour_dict)
                # Get the appropriate style from copper->pours
                style = Style(new_pour_dict, layer_name='copper', sub_item='pours')
                shape.setStyle(style)
            else:
                shape = Shape(pour_dict)
                # Get the appropriate style from copper->pours
                style = Style(pour_dict, layer_name='copper', sub_item='pours')
                shape.setStyle(style)

            # Place on all specified layers
            for layer in layers:
                place.placeShape(shape, shape_group[layer])
Example #6
0
def gerbers_to_svg(manufacturer='default'):
    """
    Takes Gerber files as input and generates an SVG of them
    """


    def normalise_gerber_number(gerber_number, axis, form):
        """
        Takes a Gerber number and converts it into a float using
        the formatting defined in the Gerber header
        """

        # TODO: actually support anything other than leading zeros
        number = gerber_number / pow(10.0, form[axis]['decimal'])
        
        return number
 

    def parsed_grammar_to_dict(parsed_grammar):
        """
        Converts the Gerber parsing results to an SVG.
        """ 
 
        gerber_dict = {}
        current_aperture = None
        new_shape = True

        for line in parsed_grammar:
            if line.dump(): 
                if (line.format):
                    if gerber_dict.get('format') is None:
                        gerber_dict['format'] = {}
                    tmp = gerber_dict['format']
                    
                    tmp['notation'] = line['format']['notation'] 
                    tmp['zeros'] = line['format']['zeros']
                    tmp['x'] = {}
                    tmp['x']['integer'] = line['format']['x']['integer']
                    tmp['x']['decimal'] = line['format']['x']['decimal']
                    tmp['y'] = {}
                    tmp['y']['integer'] = line['format']['x']['integer']
                    tmp['y']['decimal'] = line['format']['x']['decimal']
                 
                elif (line.units):
                    gerber_dict['units'] = line['units']['units']

                elif (line.aperture_definition):
                    tmp = {}
                    if line['aperture_definition']['type'] == 'circle':
                        tmp['type'] = 'circle'
                        tmp['diameter'] = line['aperture_definition']['diameter']
                        tmp['number'] = line['aperture_definition']['number']
                    elif line['aperture_definition']['type'] == 'rect':
                        tmp['type'] = 'rect'
                        tmp['width'] = line['aperture_definition']['width']
                        tmp['height'] = line['aperture_definition']['height']
                        tmp['number'] = line['aperture_definition']['number']
                    else:
                        print "ERROR: cannot recognise aperture definition type"
                    
                    if gerber_dict.get('aperture-definitions') is None:
                        gerber_dict['aperture-definitions'] = []
                    
                    gerber_dict['aperture-definitions'].append(tmp)

                elif line.polarity_change:

                    if gerber_dict.get('features') is None:
                        gerber_dict['features'] = []
  
                    polarity = line['polarity_change']['polarity']
                    polarity_dict = {}
                    polarity_dict['polarity'] = polarity
                    polarity_dict['shapes'] = []
                    gerber_dict['features'].append(polarity_dict)

                elif line.aperture_change:
                    tmp = {}
                    tmp['type'] = 'aperture-change'
                    tmp['number'] = line.aperture_change['number']
                    #if len(gerber_dict['features'][-1]['shapes'] == 0):
                    gerber_dict['features'][-1]['shapes'].append(tmp)
                    #else:    
                    #    gerber_dict['features'][-1]['shapes'].append(tmp)

                    tmp = {}
                    tmp['type'] = 'stroke'
                    tmp['segments'] = []
                    gerber_dict['features'][-1]['shapes'].append(tmp)

                elif line.start_closed_shape:
                    tmp = {}
                    tmp['type'] = 'fill'
                    tmp['segments'] = []
                    gerber_dict['features'][-1]['shapes'].append(tmp)
                
                elif line.move or line.draw or line.flash:

                    # TODO: hack alert! (Got to get shit done, you know? Don't judge me!)
                    if line.move:
                        command_name = 'move'
                        item = line.move
                    if line.draw:
                        command_name = 'draw'
                        item = line.draw
                    if line.flash:
                        command_name = 'flash'
                        item = line.flash
                  
                    point = Point(normalise_gerber_number(item['x'], 'x', gerber_dict['format']), normalise_gerber_number(item['y'], 'y', gerber_dict['format']))
                    tmp = {}
                    tmp['type'] = command_name
                    tmp['coord'] = point
                    gerber_dict['features'][-1]['shapes'][-1]['segments'].append(tmp)

                elif line.end_closed_shape:
                    new_shape = True

        return gerber_dict



    def create_gerber_svg_data(gerber_data):
        """
        Returns an SVG element of the input Gerber data
        """
        gerber_data_parsed = gerber_grammar.parseString(gerber_data)
        gerber_data_dict = parsed_grammar_to_dict(gerber_data_parsed)
        gerber_data_svg = svg.generate_svg_from_gerber_dict(gerber_data_dict)

        return gerber_data_svg





    # get the board's shape / outline
    board_shape_gerber_lp = None
    shape = config.brd['board_outline']['shape']
    board_shape_type = shape.get('type')

    if board_shape_type in ['rect', 'rectangle']:
        offset = utils.to_Point(shape.get('offset') or [0, 0])
        board_shape_path = svg.rect_to_path(shape)

    elif board_shape_type == 'path':
        board_shape_path = shape.get('value')
        board_shape_gerber_lp = shape.get('gerber_lp')
        if board_shape_path is None:
            print "ERROR: couldn't find a path under key 'value' for board outline"

    else:
        print "ERROR: unrecognised board shape type: %s. Possible options are 'rect' or 'path'" % board_shape_type

    # convert path to relative
    board_shape_path_relative = svg.absolute_to_relative_path(board_shape_path)

    # this will return a path having an origin at the center of the shape
    # defined by the path
    board_width, board_height, board_outline = svg.transform_path(board_shape_path_relative, True)

    display_width = board_width
    display_height = board_height

    #transform = 'translate(' + str(round((board_width)/2, SD)) + ' ' + str(round((board_height)/2, SD)) + ')'
    sig_dig = config.cfg['significant-digits']
    #transform = 'translate(%s %s)' % (round(board_width/2, sig_dig),
    #                                  round(board_height/2, sig_dig))

    # extra buffer for display frame
    display_frame_buffer = config.cfg.get('display-frame-buffer') or 1.0

    gerber = et.Element('svg',
        width=str(display_width) + config.brd['config']['units'],
        height=str(display_height) + config.brd['config']['units'],
        viewBox=str(-display_frame_buffer/2) + ' ' + str(-display_frame_buffer/2) + ' ' + str(board_width+display_frame_buffer) + ' ' + str(board_height + display_frame_buffer),
        version='1.1',
        nsmap=cfg['namespace'],
        fill='black')

    doc = et.ElementTree(gerber)

    gerber_layers = svg.create_layers_for_gerber_svg(gerber)


    # directory for where to expect the Gerbers within the build path
    # regardless of the source of the Gerbers, the PCBmodE directory
    # structure is assumed
    production_path = os.path.join(config.cfg['base-dir'],
                                   config.cfg['locations']['build'], 
                                   'production')

    # get board information from configuration file
    pcbmode_version = config.cfg['version']
    board_name = config.cfg['name']
    board_revision = config.brd['config'].get('rev')

    base_name = "%s_rev_%s" % (board_name, board_revision)

    gerber_grammar = gerber_grammar_generator()



    for foil in ['outline']:#, 'documentation']:
        gerber_file = os.path.join(production_path, base_name + '_%s.ger'% (foil))
        gerber_data = open(gerber_file, 'r').read()
        gerber_svg = create_gerber_svg_data(gerber_data)
        gerber_svg_layer = gerber_layers[foil]['layer']
        gerber_svg_layer.append(gerber_svg)
        print foil

        
    for pcb_layer in utils.getSurfaceLayers():
        for foil in ['copper', 'silkscreen', 'soldermask']:
            gerber_file = os.path.join(production_path, 
                                       base_name + '_%s_%s.ger'% (pcb_layer, foil))
            gerber_data = open(gerber_file, 'r').read()
            gerber_svg = create_gerber_svg_data(gerber_data)
            gerber_svg_layer = gerber_layers[pcb_layer][foil]['layer']
            gerber_svg_layer.append(gerber_svg)
            print foil


    output_file = os.path.join(config.cfg['base-dir'], config.cfg['locations']['build'], cfg['board_name'] + '_gerber.svg')

    try:
        f = open(output_file, 'wb')
    except IOError as e:
        print "I/O error({0}): {1}".format(e.errno, e.strerror)

    f.write(et.tostring(doc, pretty_print=True))
    f.close()


    return
Example #7
0
    def __init__(self, module_dict, routing_dict, asmodule=False):
        """
        """

        self._module_dict = module_dict
        self._routing_dict = routing_dict

        self._outline = self._getOutline()
        self._width = self._outline.getWidth()
        self._height = self._outline.getHeight()

        # Get dictionary of component definitions
        components_dict = self._module_dict.get('components') or {}
        self._components = self._getComponents(components_dict)

        # Get dictionary of component definitions
        vias_dict = self._routing_dict.get('vias') or {}
        self._vias = self._getComponents(vias_dict)

        sig_dig = config.cfg['significant-digits']
        self._transform = 'translate(%s %s)' % (round(
            self._width / 2, sig_dig), round(self._height / 2, sig_dig))

        # Create the Inkscape SVG document
        self._module = self._getModuleElement()
        svg_doc = et.ElementTree(self._module)

        # Get a dictionary of SVG layers
        self._layers = svg.makeSvgLayers(self._module, self._transform)

        # Add a 'defs' element:
        #   http://www.w3.org/TR/SVG/struct.html#Head
        # This is where masking elements that are used for pours are stored
        defs = et.SubElement(self._module, 'defs')
        self._masks = {}
        for pcb_layer in utils.getSurfaceLayers():
            element = et.SubElement(defs,
                                    'mask',
                                    id="mask-%s" % pcb_layer,
                                    transform=self._transform)
            # This will identify the masks for each PCB layer when
            # the layer is converted to Gerber
            element.set('{' + config.cfg['ns']['pcbmode'] + '}pcb-layer',
                        pcb_layer)
            self._masks[pcb_layer] = element

        self._placeOutline()
        self._placeOutlineDimensions()

        msg.subInfo('Placing components:', newline=False)
        self._placeComponents(self._components,
                              'components',
                              print_refdef=True)
        print

        msg.subInfo('Placing routes')
        self._placeRouting()

        msg.subInfo('Placing vias')
        self._placeComponents(self._vias, 'vias')

        msg.subInfo('Placing shapes')
        self._placeShapes()

        msg.subInfo('Placing pours')
        self._placePours()

        if config.tmp['no-docs'] == False:
            msg.subInfo('Placing documentation')
            self._placeDocs()

        if config.tmp['no-drill-index'] == False:
            msg.subInfo('Placing drill index')
            self._placeDrillIndex()

        if config.tmp['no-layer-index'] == False:
            msg.subInfo('Placing layer index')
            self._placeLayerIndex()

        # This 'cover' "enables" the mask shapes defined in the mask are
        # shown. It *must* be the last element in the mask definition;
        # any mask element after it won't show
        for pcb_layer in utils.getSurfaceLayers():
            if utils.checkForPoursInLayer(pcb_layer) is True:
                mask_cover = et.SubElement(self._masks[pcb_layer],
                                           'rect',
                                           x="%s" % str(-self._width / 2),
                                           y="%s" % str(-self._height / 2),
                                           width="%s" % self._width,
                                           height="%s" % self._height,
                                           style="fill:#fff;")
                # This tells the Gerber conversion to ignore this shape
                mask_cover.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                               'mask-cover')

        output_file = os.path.join(config.cfg['base-dir'],
                                   config.cfg['locations']['build'],
                                   config.cfg['name'] + '.svg')

        try:
            f = open(output_file, 'wb')
        except IOError as e:
            print "I/O error({0}): {1}".format(e.errno, e.strerror)

        f.write(et.tostring(svg_doc, pretty_print=True))
        f.close()
Example #8
0
def extractComponents(svg_in):
    """
    """

    # Get copper refdef shape groups from SVG data
    xpath_expr_copper_pads = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="copper"]//svg:g[@pcbmode:sheet="pads"]//svg:g[@pcbmode:refdef]'

    xpath_expr_refdefs = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="silkscreen"]//svg:g[@pcbmode:type="refdef"][@pcbmode:refdef="%s"]'

    for pcb_layer in utils.getSurfaceLayers():
        shapes = svg_in.findall(xpath_expr_copper_pads % pcb_layer,
                                namespaces={
                                    'pcbmode': config.cfg['ns']['pcbmode'],
                                    'svg': config.cfg['ns']['svg']
                                })

        for shape in shapes:
            transform_data = utils.parseTransform(shape.get('transform'))
            refdef = shape.get('{' + config.cfg['ns']['pcbmode'] + '}refdef')
            comp_dict = config.brd['components'][refdef]

            # Check if the copper shapes are on the same layer as placement.
            # Ignore if otherwise. While it is possible that a component is placed
            # on one layer but all its components are on another, it is very
            # unlikely, and doesn't make much sense.
            on_layer = comp_dict.get('layer') or 'top'
            if pcb_layer == on_layer:
                new_location = transform_data['location']
                old_location = utils.toPoint(
                    comp_dict.get('location') or [0, 0])
                # Invert 'y' coordinate
                new_location.y *= config.cfg['invert-y']

                # Change component location if needed
                if new_location != old_location:
                    msg.subInfo("Component %s moved from %s to %s" %
                                (refdef, old_location, new_location))
                    comp_dict['location'] = [new_location.x, new_location.y]

                # Change component rotation if needed
                if transform_data['type'] == 'matrix':
                    old_rotate = comp_dict.get('rotate') or 0
                    new_rotate = transform_data['rotate']
                    comp_dict['rotate'] = round(
                        (old_rotate + new_rotate) % 360, 4)
                    msg.subInfo("Component %s rotated from %s to %s" %
                                (refdef, old_rotate, comp_dict['rotate']))

            refdef_texts = svg_in.findall(xpath_expr_refdefs %
                                          (pcb_layer, refdef),
                                          namespaces={
                                              'pcbmode':
                                              config.cfg['ns']['pcbmode'],
                                              'svg':
                                              config.cfg['ns']['svg']
                                          })

            for refdef_text in refdef_texts:

                # Get refdef group location
                transform_data = utils.parseTransform(
                    refdef_text.get('transform'))
                group_loc = transform_data['location']
                # Invert 'y' coordinate
                group_loc.y *= config.cfg['invert-y']

                # Get the refdef text path inside the refdef group
                refdef_path = refdef_text.find(
                    "svg:path", namespaces={'svg': config.cfg['ns']['svg']})
                try:
                    transform_data = utils.parseTransform(
                        refdef_path.get('transform'))
                except:
                    transform_data['location'] = Point(0, 0)
                refdef_loc = transform_data['location']
                # Invert 'y' coordinate
                refdef_loc.y *= config.cfg['invert-y']

                # Current ('old') location of the component
                current_component_loc = utils.toPoint(
                    comp_dict.get('location', [0, 0]))
                try:
                    current_refdef_loc = utils.toPoint(
                        comp_dict['silkscreen']['refdef']['location'])
                except:
                    current_refdef_loc = Point()

                # TODO: this is an embarassing mess, but I have too much of
                # a headache to tidy it up!
                if pcb_layer == 'bottom':
                    current_refdef_loc.x = -current_refdef_loc.x
                diff = group_loc - current_component_loc
                new_refdef_loc = diff + current_refdef_loc
                if pcb_layer == 'bottom':
                    new_refdef_loc.x = -new_refdef_loc.x

                # Change the location in the dictionary
                if new_refdef_loc != current_refdef_loc:
                    try:
                        tmp = comp_dict['silkscreen']
                    except:
                        comp_dict['silkscreen'] = {}

                    try:
                        tmp = comp_dict['silkscreen']['refdef']
                    except:
                        comp_dict['silkscreen']['refdef'] = {}

                    comp_dict['silkscreen']['refdef']['location'] = [
                        new_refdef_loc.x, new_refdef_loc.y
                    ]

    # Save board config to file (everything is saved, not only the
    # component data)
    filename = os.path.join(config.cfg['locations']['boards'],
                            config.cfg['name'], config.cfg['name'] + '.json')
    try:
        with open(filename, 'wb') as f:
            f.write(json.dumps(config.brd, sort_keys=True, indent=2))
    except:
        msg.error("Cannot save file %s" % filename)

    return
Example #9
0
def extractComponents(svg_in):
    """
    """
    
    # Get copper refdef shape groups from SVG data
    xpath_expr_copper_pads = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="copper"]//svg:g[@pcbmode:sheet="pads"]//svg:g[@pcbmode:refdef]'

    xpath_expr_refdefs = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="silkscreen"]//svg:g[@pcbmode:type="refdef"][@pcbmode:refdef="%s"]'

    for pcb_layer in utils.getSurfaceLayers():
        shapes = svg_in.findall(xpath_expr_copper_pads % pcb_layer, 
                                namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                            'svg':config.cfg['ns']['svg']})

        for shape in shapes:
            transform_data = utils.parseTransform(shape.get('transform'))
            refdef = shape.get('{'+config.cfg['ns']['pcbmode']+'}refdef')
            comp_dict = config.brd['components'][refdef]

            # Check if the copper shapes are on the same layer as placement.
            # Ignore if otherwise. While it is possible that a component is placed
            # on one layer but all its components are on another, it is very
            # unlikely, and doesn't make much sense.
            on_layer = comp_dict.get('layer') or 'top'
            if pcb_layer == on_layer:
                new_location = transform_data['location']
                old_location = utils.toPoint(comp_dict.get('location') or [0, 0])
                # Invert 'y' coordinate
                new_location.y *= config.cfg['invert-y']

                # Change component location if needed
                if new_location != old_location:
                    msg.subInfo("Component %s moved from %s to %s" % (refdef, 
                                                                      old_location, 
                                                                      new_location))
                    comp_dict['location'] = [new_location.x, 
                                             new_location.y]

                # Change component rotation if needed
                if transform_data['type'] == 'matrix':
                    old_rotate = comp_dict.get('rotate') or 0
                    new_rotate = transform_data['rotate']
                    comp_dict['rotate'] = round((old_rotate+new_rotate) % 360,4)
                    msg.subInfo("Component %s rotated from %s to %s" % (refdef, 
                                                                        old_rotate, 
                                                                        comp_dict['rotate']))

            refdef_texts = svg_in.findall(xpath_expr_refdefs % (pcb_layer, refdef), 
                                          namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                                      'svg':config.cfg['ns']['svg']})

            for refdef_text in refdef_texts:

                # Get refdef group location
                transform_data = utils.parseTransform(refdef_text.get('transform'))
                group_loc = transform_data['location']
                # Invert 'y' coordinate
                group_loc.y *= config.cfg['invert-y']


                # Get the refdef text path inside the refdef group
                refdef_path = refdef_text.find("svg:path", 
                                               namespaces={'svg':config.cfg['ns']['svg']})
                try:
                    transform_data = utils.parseTransform(refdef_path.get('transform'))
                except:
                    transform_data['location'] = Point(0,0)
                refdef_loc = transform_data['location']
                # Invert 'y' coordinate
                refdef_loc.y *= config.cfg['invert-y']

                # Current ('old') location of the component
                current_component_loc = utils.toPoint(comp_dict.get('location', [0, 0]))
                try:
                    current_refdef_loc = utils.toPoint(comp_dict['silkscreen']['refdef']['location'])
                except:
                    current_refdef_loc = Point()

                # TODO: this is an embarassing mess, but I have too much of
                # a headache to tidy it up!
                if pcb_layer == 'bottom':
                    current_refdef_loc.x = -current_refdef_loc.x
                diff = group_loc-current_component_loc
                new_refdef_loc = diff + current_refdef_loc
                if pcb_layer == 'bottom':
                    new_refdef_loc.x = -new_refdef_loc.x

                # Change the location in the dictionary
                if new_refdef_loc != current_refdef_loc:
                    try:
                        tmp = comp_dict['silkscreen']
                    except:
                        comp_dict['silkscreen'] = {}

                    try:
                        tmp = comp_dict['silkscreen']['refdef']
                    except:
                        comp_dict['silkscreen']['refdef'] = {}

                    comp_dict['silkscreen']['refdef']['location'] = [new_refdef_loc.x,
                                                                     new_refdef_loc.y] 


    # Save board config to file (everything is saved, not only the
    # component data)
    filename = os.path.join(config.cfg['locations']['boards'], 
                            config.cfg['name'], 
                            config.cfg['name'] + '.json')
    try:
        with open(filename, 'wb') as f:
            f.write(json.dumps(config.brd, sort_keys=True, indent=2))
    except:
        msg.error("Cannot save file %s" % filename)
 
    return
Example #10
0
    def _placeLayerIndex(self):
        """
        Adds a drill index
        """

        text_dict = config.stl['layout']['layer-index']['text']
        text_dict['type'] = 'text'

        # Set the height (and width) of the rectangle (square) to the
        # size of the text
        rect_width = utils.parseDimension(text_dict['font-size'])[0]
        rect_height = rect_width
        rect_gap = 0.25

        # Get location, or generate one
        try:
            location = config.brd['layer-index']['location']
        except:
            # If not location is specified, put the drill index at the
            # top right of the board. The 'gap' defines the extra
            # spcae between the top of the largest drill and the
            # board's edge
            gap = 2
            location = [
                self._width / 2 + gap, self._height / 2 - rect_height / 2
            ]
        location = utils.toPoint(location)

        rect_dict = {}
        rect_dict['type'] = 'rect'
        rect_dict['style'] = 'fill'
        rect_dict['width'] = rect_width
        rect_dict['height'] = rect_height

        # Create group for placing index
        for pcb_layer in utils.getSurfaceLayers():
            for sheet in [
                    'copper', 'soldermask', 'silkscreen', 'assembly',
                    'solderpaste'
            ]:
                layer = self._layers[pcb_layer][sheet]['layer']
                transform = "translate(%s,%s)" % (
                    location.x, config.cfg['invert-y'] * location.y)
                group = et.SubElement(layer, 'g', transform=transform)
                group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                          'layer-index')

                rect_shape = Shape(rect_dict)
                style = Style(rect_dict, sheet)
                rect_shape.setStyle(style)
                place.placeShape(rect_shape, group)

                text_dict['value'] = "%s %s" % (pcb_layer, sheet)
                #text_dict['location'] = [rect_width+rect_gap+text_width, 0]
                text_shape = Shape(text_dict)
                text_width = text_shape.getWidth()
                style = Style(text_dict, sheet)
                text_shape.setStyle(style)
                element = place.placeShape(text_shape, group)
                element.set(
                    "transform", "translate(%s,%s)" %
                    (rect_width / 2 + rect_gap + text_width / 2, 0))

                location.y += config.cfg['invert-y'] * (rect_height + rect_gap)

            location.y += config.cfg['invert-y'] * (rect_height + rect_gap * 2)
Example #11
0
def gerbers_to_svg(manufacturer='default'):
    """
    Takes Gerber files as input and generates an SVG of them
    """
    def normalise_gerber_number(gerber_number, axis, form):
        """
        Takes a Gerber number and converts it into a float using
        the formatting defined in the Gerber header
        """

        # TODO: actually support anything other than leading zeros
        number = gerber_number / pow(10.0, form[axis]['decimal'])

        return number

    def parsed_grammar_to_dict(parsed_grammar):
        """
        Converts the Gerber parsing results to an SVG.
        """

        gerber_dict = {}
        current_aperture = None
        new_shape = True

        for line in parsed_grammar:
            if line.dump():
                if (line.format):
                    if gerber_dict.get('format') is None:
                        gerber_dict['format'] = {}
                    tmp = gerber_dict['format']

                    tmp['notation'] = line['format']['notation']
                    tmp['zeros'] = line['format']['zeros']
                    tmp['x'] = {}
                    tmp['x']['integer'] = line['format']['x']['integer']
                    tmp['x']['decimal'] = line['format']['x']['decimal']
                    tmp['y'] = {}
                    tmp['y']['integer'] = line['format']['x']['integer']
                    tmp['y']['decimal'] = line['format']['x']['decimal']

                elif (line.units):
                    gerber_dict['units'] = line['units']['units']

                elif (line.aperture_definition):
                    tmp = {}
                    if line['aperture_definition']['type'] == 'circle':
                        tmp['type'] = 'circle'
                        tmp['diameter'] = line['aperture_definition'][
                            'diameter']
                        tmp['number'] = line['aperture_definition']['number']
                    elif line['aperture_definition']['type'] == 'rect':
                        tmp['type'] = 'rect'
                        tmp['width'] = line['aperture_definition']['width']
                        tmp['height'] = line['aperture_definition']['height']
                        tmp['number'] = line['aperture_definition']['number']
                    else:
                        print "ERROR: cannot recognise aperture definition type"

                    if gerber_dict.get('aperture-definitions') is None:
                        gerber_dict['aperture-definitions'] = []

                    gerber_dict['aperture-definitions'].append(tmp)

                elif line.polarity_change:

                    if gerber_dict.get('features') is None:
                        gerber_dict['features'] = []

                    polarity = line['polarity_change']['polarity']
                    polarity_dict = {}
                    polarity_dict['polarity'] = polarity
                    polarity_dict['shapes'] = []
                    gerber_dict['features'].append(polarity_dict)

                elif line.aperture_change:
                    tmp = {}
                    tmp['type'] = 'aperture-change'
                    tmp['number'] = line.aperture_change['number']
                    #if len(gerber_dict['features'][-1]['shapes'] == 0):
                    gerber_dict['features'][-1]['shapes'].append(tmp)
                    #else:
                    #    gerber_dict['features'][-1]['shapes'].append(tmp)

                    tmp = {}
                    tmp['type'] = 'stroke'
                    tmp['segments'] = []
                    gerber_dict['features'][-1]['shapes'].append(tmp)

                elif line.start_closed_shape:
                    tmp = {}
                    tmp['type'] = 'fill'
                    tmp['segments'] = []
                    gerber_dict['features'][-1]['shapes'].append(tmp)

                elif line.move or line.draw or line.flash:

                    # TODO: hack alert! (Got to get shit done, you know? Don't judge me!)
                    if line.move:
                        command_name = 'move'
                        item = line.move
                    if line.draw:
                        command_name = 'draw'
                        item = line.draw
                    if line.flash:
                        command_name = 'flash'
                        item = line.flash

                    point = Point(
                        normalise_gerber_number(item['x'], 'x',
                                                gerber_dict['format']),
                        normalise_gerber_number(item['y'], 'y',
                                                gerber_dict['format']))
                    tmp = {}
                    tmp['type'] = command_name
                    tmp['coord'] = point
                    gerber_dict['features'][-1]['shapes'][-1][
                        'segments'].append(tmp)

                elif line.end_closed_shape:
                    new_shape = True

        return gerber_dict

    def create_gerber_svg_data(gerber_data):
        """
        Returns an SVG element of the input Gerber data
        """
        gerber_data_parsed = gerber_grammar.parseString(gerber_data)
        gerber_data_dict = parsed_grammar_to_dict(gerber_data_parsed)
        gerber_data_svg = svg.generate_svg_from_gerber_dict(gerber_data_dict)

        return gerber_data_svg

    # get the board's shape / outline
    board_shape_gerber_lp = None
    shape = config.brd['board_outline']['shape']
    board_shape_type = shape.get('type')

    if board_shape_type in ['rect', 'rectangle']:
        offset = utils.to_Point(shape.get('offset') or [0, 0])
        board_shape_path = svg.rect_to_path(shape)

    elif board_shape_type == 'path':
        board_shape_path = shape.get('value')
        board_shape_gerber_lp = shape.get('gerber_lp')
        if board_shape_path is None:
            print "ERROR: couldn't find a path under key 'value' for board outline"

    else:
        print "ERROR: unrecognised board shape type: %s. Possible options are 'rect' or 'path'" % board_shape_type

    # convert path to relative
    board_shape_path_relative = svg.absolute_to_relative_path(board_shape_path)

    # this will return a path having an origin at the center of the shape
    # defined by the path
    board_width, board_height, board_outline = svg.transform_path(
        board_shape_path_relative, True)

    display_width = board_width
    display_height = board_height

    #transform = 'translate(' + str(round((board_width)/2, SD)) + ' ' + str(round((board_height)/2, SD)) + ')'
    sig_dig = config.cfg['significant-digits']
    #transform = 'translate(%s %s)' % (round(board_width/2, sig_dig),
    #                                  round(board_height/2, sig_dig))

    # extra buffer for display frame
    display_frame_buffer = config.cfg.get('display-frame-buffer') or 1.0

    gerber = et.Element(
        'svg',
        width=str(display_width) + config.brd['config']['units'],
        height=str(display_height) + config.brd['config']['units'],
        viewBox=str(-display_frame_buffer / 2) + ' ' +
        str(-display_frame_buffer / 2) + ' ' +
        str(board_width + display_frame_buffer) + ' ' +
        str(board_height + display_frame_buffer),
        version='1.1',
        nsmap=cfg['namespace'],
        fill='black')

    doc = et.ElementTree(gerber)

    gerber_layers = svg.create_layers_for_gerber_svg(gerber)

    # directory for where to expect the Gerbers within the build path
    # regardless of the source of the Gerbers, the PCBmodE directory
    # structure is assumed
    production_path = os.path.join(config.cfg['base-dir'],
                                   config.cfg['locations']['build'],
                                   'production')

    # get board information from configuration file
    pcbmode_version = config.cfg['version']
    board_name = config.cfg['name']
    board_revision = config.brd['config'].get('rev')

    base_name = "%s_rev_%s" % (board_name, board_revision)

    gerber_grammar = gerber_grammar_generator()

    for foil in ['outline']:  #, 'documentation']:
        gerber_file = os.path.join(production_path,
                                   base_name + '_%s.ger' % (foil))
        gerber_data = open(gerber_file, 'r').read()
        gerber_svg = create_gerber_svg_data(gerber_data)
        gerber_svg_layer = gerber_layers[foil]['layer']
        gerber_svg_layer.append(gerber_svg)
        print foil

    for pcb_layer in utils.getSurfaceLayers():
        for foil in ['copper', 'silkscreen', 'soldermask']:
            gerber_file = os.path.join(
                production_path, base_name + '_%s_%s.ger' % (pcb_layer, foil))
            gerber_data = open(gerber_file, 'r').read()
            gerber_svg = create_gerber_svg_data(gerber_data)
            gerber_svg_layer = gerber_layers[pcb_layer][foil]['layer']
            gerber_svg_layer.append(gerber_svg)
            print foil

    output_file = os.path.join(config.cfg['base-dir'],
                               config.cfg['locations']['build'],
                               cfg['board_name'] + '_gerber.svg')

    try:
        f = open(output_file, 'wb')
    except IOError as e:
        print "I/O error({0}): {1}".format(e.errno, e.strerror)

    f.write(et.tostring(doc, pretty_print=True))
    f.close()

    return
Example #12
0
    def _placeShapes(self):
        """
        """

        mirror = False

        try:
            shapes_dict = self._module_dict['shapes']
        except:
            return

        for sheet in ['copper', 'soldermask', 'solderpaste', 'silkscreen']:
            try:
                shapes = shapes_dict[sheet]
            except KeyError:
                continue

            there_are_pours = {}
            shape_groups = {}
            for pcb_layer in utils.getSurfaceLayers():
                there_are_pours[pcb_layer] = utils.checkForPoursInLayer(
                    pcb_layer)
                shape_groups[pcb_layer] = et.SubElement(
                    self._layers[pcb_layer][sheet]['layer'], 'g')
                shape_groups[pcb_layer].set(
                    '{' + config.cfg['ns']['pcbmode'] + '}type',
                    'module-shapes')

            for shape_dict in shapes:

                pcb_layers = shape_dict.get('layers') or ['top']

                for pcb_layer in pcb_layers:

                    # Shapes placed on the bottom layer are not mirrored
                    # unless they are text, in which case, it's the
                    # expected behaviour and so it is mirrored by default
                    # unless otherwise instructed.
                    try:
                        shape_mirror = shape_dict.get['mirror']
                    except:
                        if shape_dict['type'] == 'text':
                            shape_mirror = True
                        else:
                            shape_mirror = False

                    if (pcb_layer == 'bottom') and (shape_mirror != False):
                        shape_dict['location'][0] *= -1
                        mirror = True
                    else:
                        mirror = False

                    shape = Shape(shape_dict)
                    style = Style(shape_dict, sheet)
                    shape.setStyle(style)
                    place.placeShape(shape, shape_groups[pcb_layer], mirror)

                    # Place mask for pour if copper shape
                    if (sheet == 'copper') and (there_are_pours[pcb_layer]
                                                == True):
                        location = shape.getLocation()
                        transform = "translate(%s,%s)" % (location.x,
                                                          location.y)
                        mask_group = et.SubElement(self._masks[pcb_layer], 'g')
                        self._placeMask(svg_layer=mask_group,
                                        shape=shape,
                                        kind='pad',
                                        original=False,
                                        mirror=mirror)
Example #13
0
    def _placeRouting(self):
        """
        """

        routing = config.rte
        routes = routing.get('routes') or {}
        vias = routing.get('vias')

        # Path effects are used for meandering paths, for example
        path_effects = routes.get('path_effects')

        xpath_expr = "//g[@inkscape:label='%s']//g[@inkscape:label='%s']"
        extra_attributes = [
            'inkscape:connector-curvature', 'inkscape:original-d',
            'inkscape:path-effect'
        ]

        for pcb_layer in utils.getSurfaceLayers():

            # Are there pours in the layer? This makes a difference for whether to place
            # masks
            there_are_pours = utils.checkForPoursInLayer(pcb_layer)

            # Define a group where masks are stored
            mask_group = et.SubElement(self._masks[pcb_layer], 'g')

            # Place defined routes on this SVG layer
            sheet = self._layers[pcb_layer]['copper']['routing']['layer']

            for route_key in (routes.get(pcb_layer) or {}):
                shape_dict = routes[pcb_layer][route_key]
                shape = Shape(shape_dict)
                style = Style(shape_dict, 'copper')
                shape.setStyle(style)

                # Routes are a special case where they are used as-is
                # counting on Inkscapes 'optimised' setting to modify
                # the path such that placement is refleced in
                # it. Therefor we use the original path, not the
                # transformed one as usual
                use_original_path = True
                mirror_path = False
                route_element = place.placeShape(shape, sheet, mirror_path,
                                                 use_original_path)

                route_element.set('style', shape.getStyleString())

                # Set the key as pcbmode:id of the route. This is used
                # when extracting routing to offset the location of a
                # modified route
                route_element.set(
                    '{' + config.cfg['ns']['pcbmode'] + '}%s' % ('id'),
                    route_key)

                # Add a custom buffer definition if it exists
                custom_buffer = shape_dict.get('buffer-to-pour')
                if custom_buffer != None:
                    route_element.set(
                        '{' + config.cfg['namespace']['pcbmode'] +
                        '}%s' % "buffer-to-pour", str(custom_buffer))

                # TODO: can this be done more elegantly, and "general purpose"?
                for extra_attrib in extra_attributes:
                    ea = shape_dict.get(extra_attrib)
                    if ea is not None:
                        route_element.set(
                            '{' + config.cfg['namespace']['inkscape'] + '}%s' %
                            (extra_attrib[extra_attrib.index(':') + 1:], ea))

                # TODO: this needs to eventually go away or be done properly
                pcbmode_params = shape_dict.get('pcbmode')
                if pcbmode_params is not None:
                    route_element.set('pcbmode', pcbmode_params)

                if ((there_are_pours == True) and (custom_buffer != "0")):
                    self._placeMask(self._masks[pcb_layer], shape, 'route',
                                    use_original_path)
Example #14
0
def gerberise(manufacturer='default'):
    """
    Generate Gerbers for one or more layers
    """

    # Open the board's SVG
    svg_in = utils.openBoardSVG()

    # Get Gerber generation settings
    gcd = config.brd['gerber']
    decimals = gcd['decimals']
    digits = gcd['digits'] 
    steps = gcd['steps-per-segment']
    length = gcd['min-segment-length']

    # Get layer data
    xpath_regex = ""
    ns = {'pcbmode':config.cfg['ns']['pcbmode'],
          'svg':config.cfg['ns']['svg']} 


    # Save to file
    base_dir = os.path.join(config.cfg['base-dir'], 
                            config.cfg['locations']['build'], 
                            'production')
    # Create directory if it doesn't exist already
    utils.create_dir(base_dir)
    base_name = "%s_rev_%s" % (config.brd['config']['name'],
                               config.brd['config']['rev'])
 
    filename_info = config.cfg['manufacturers'][manufacturer]['filenames']['gerbers']

    # Process Gerbers for PCB layers and sheets
    for pcb_layer in utils.getSurfaceLayers():

        # Get the SVG layer corresponding to the PCB layer
        svg_layer = svg_in.find("//svg:g[@pcbmode:pcb-layer='%s']" % (pcb_layer),
                                namespaces=ns)

        # Get masks (must be placed right after pours)
        mask_paths = svg_in.findall(".//svg:defs//svg:mask[@pcbmode:pcb-layer='%s']//svg:path" % pcb_layer,
                                       namespaces=ns)

        sheets = ['copper', 'soldermask', 'solderpaste', 'silkscreen']
        for sheet in sheets:
            # Get the SVG layer corresponding to the 'sheet'
            sheet_layer = svg_layer.find(".//svg:g[@pcbmode:sheet='%s']" % (sheet),
                                         namespaces=ns)

            if sheet == 'copper':
                mask_paths_to_pass = mask_paths
            else:
                mask_paths_to_pass = []

            # Create a Gerber object
            gerber = Gerber(sheet_layer,
                            mask_paths_to_pass,
                            decimals,
                            digits,
                            steps,
                            length)

            try:
                ext = filename_info[pcb_layer][sheet]['ext']
            except KeyError:
                ext = 'ger'
            add = '_%s_%s.%s' % (pcb_layer, sheet, ext)
            filename = os.path.join(base_dir, base_name + add)

            with open(filename, "wb") as f:
                for line in gerber.getGerber():
                    f.write(line)


    # Process module sheets
    sheets = ['outline', 'documentation']
    for sheet in sheets:
        # Get the SVG layer corresponding to the 'sheet'
        sheet_layer = svg_in.find(".//svg:g[@pcbmode:sheet='%s']" % (sheet),
                                  namespaces=ns)            

        # Create a Gerber object
        gerber = Gerber(sheet_layer,
                        [],
                        decimals,
                        digits,
                        steps,
                        length)

        add = '_%s.%s' % (sheet,
                          filename_info['other'][sheet].get('ext') or 'ger')
        filename = os.path.join(base_dir, base_name + add)

        with open(filename, "wb") as f:
            for line in gerber.getGerber(False):
                f.write(line)


    return ['bullshit']
Example #15
0
    def __init__(self, 
                 module_dict, 
                 routing_dict, 
                 asmodule=False):
        """
        """

        self._module_dict = module_dict
        self._routing_dict = routing_dict

        self._outline = self._getOutline()
        self._width = self._outline.getWidth()
        self._height = self._outline.getHeight()

        # Get dictionary of component definitions
        components_dict = self._module_dict.get('components') or {}
        self._components = self._getComponents(components_dict)

        # Get dictionary of component definitions
        vias_dict = self._routing_dict.get('vias') or {}
        self._vias = self._getComponents(vias_dict)

        sig_dig = config.cfg['significant-digits']
        self._transform = 'translate(%s %s)' % (round(self._width/2, sig_dig),
                                                round(self._height/2, sig_dig))

        # Create the Inkscape SVG document
        self._module = self._getModuleElement()
        svg_doc = et.ElementTree(self._module)

        # Get a dictionary of SVG layers
        self._layers = svg.makeSvgLayers(self._module, self._transform)

        # Add a 'defs' element:
        #   http://www.w3.org/TR/SVG/struct.html#Head
        # This is where masking elements that are used for pours are stored
        defs = et.SubElement(self._module, 'defs')
        self._masks = {}
        for pcb_layer in utils.getSurfaceLayers():
             element = et.SubElement(defs, 'mask',
                                     id="mask-%s" % pcb_layer,
                                     transform=self._transform)
             # This will identify the masks for each PCB layer when
             # the layer is converted to Gerber
             element.set('{'+config.cfg['ns']['pcbmode']+'}pcb-layer', pcb_layer)
             self._masks[pcb_layer] = element

        self._placeOutline()
        self._placeOutlineDimensions()

        msg.subInfo('Placing components:', newline=False)
        self._placeComponents(self._components, 'components', print_refdef=True)
        print

        msg.subInfo('Placing routes')
        self._placeRouting()

        msg.subInfo('Placing vias')
        self._placeComponents(self._vias, 'vias')

        msg.subInfo('Placing shapes')
        self._placeShapes()

        msg.subInfo('Placing pours')
        self._placePours()

        if config.tmp['no-docs'] == False:
            msg.subInfo('Placing documentation')
            self._placeDocs()

        if config.tmp['no-drill-index'] == False:
            msg.subInfo('Placing drill index')
            self._placeDrillIndex()

        if config.tmp['no-layer-index'] == False:
            msg.subInfo('Placing layer index')
            self._placeLayerIndex()


        # This 'cover' "enables" the mask shapes defined in the mask are
        # shown. It *must* be the last element in the mask definition;
        # any mask element after it won't show
        for pcb_layer in utils.getSurfaceLayers():
            if utils.checkForPoursInLayer(pcb_layer) is True:
                mask_cover = et.SubElement(self._masks[pcb_layer], 'rect',
                                           x="%s" % str(-self._width/2), 
                                           y="%s" % str(-self._height/2),
                                           width="%s" % self._width,
                                           height="%s" % self._height,
                                           style="fill:#fff;")
                # This tells the Gerber conversion to ignore this shape
                mask_cover.set('{'+config.cfg['ns']['pcbmode']+'}type', 'mask-cover')

        output_file = os.path.join(config.cfg['base-dir'],
                                   config.cfg['locations']['build'],
                                   config.cfg['name'] + '.svg')
     
        try:
            f = open(output_file, 'wb')
        except IOError as e:
            print "I/O error({0}): {1}".format(e.errno, e.strerror)
     
        f.write(et.tostring(svg_doc, pretty_print=True))
        f.close()
Example #16
0
    def _placeShapes(self):
        """
        """

        mirror = False

        try:
            shapes_dict = self._module_dict['shapes']
        except:
            return


        for sheet in ['copper','soldermask','solderpaste','silkscreen']:
            try:
                shapes = shapes_dict[sheet]
            except KeyError:
                continue

            there_are_pours = {}
            shape_groups = {}
            for pcb_layer in utils.getSurfaceLayers():
                there_are_pours[pcb_layer] = utils.checkForPoursInLayer(pcb_layer)
                shape_groups[pcb_layer] = et.SubElement(self._layers[pcb_layer][sheet]['layer'], 'g')
                shape_groups[pcb_layer].set('{'+config.cfg['ns']['pcbmode']+'}type', 'module-shapes')


            for shape_dict in shapes:

                pcb_layers = shape_dict.get('layers') or ['top']

                for pcb_layer in pcb_layers:

                    # Shapes placed on the bottom layer are not mirrored
                    # unless they are text, in which case, it's the
                    # expected behaviour and so it is mirrored by default
                    # unless otherwise instructed.
                    try:
                        shape_mirror = shape_dict.get['mirror']
                    except:
                        if shape_dict['type'] == 'text':
                            shape_mirror = True
                        else:
                            shape_mirror = False
                        
                    if (pcb_layer == 'bottom') and (shape_mirror != False):
                        mirror = True
                    else:
                        mirror = False

                    shape = Shape(shape_dict)
                    style = Style(shape_dict, sheet)
                    shape.setStyle(style)
                    place.placeShape(shape, shape_groups[pcb_layer], mirror)
     
                    # Place mask for pour if copper shape
                    if (sheet == 'copper') and (there_are_pours[pcb_layer] == True):
                        location = shape.getLocation()
                        transform = "translate(%s,%s)" % (location.x, location.y)
                        mask_group = et.SubElement(self._masks[pcb_layer], 'g')
                                                   #id="routing_masks") 
                                                   #transform=transform)
                        self._placeMask(svg_layer=mask_group, 
                                        shape=shape,
                                        kind='pad',
                                        original=False,
                                        mirror=False)
Example #17
0
    def _placeRouting(self):
        """
        """

        routing = config.rte
        routes = routing.get('routes') or {}
        vias = routing.get('vias') 

        # Path effects are used for meandering paths, for example
        path_effects = routes.get('path_effects')
 
        xpath_expr = "//g[@inkscape:label='%s']//g[@inkscape:label='%s']"
        extra_attributes = ['inkscape:connector-curvature', 'inkscape:original-d', 'inkscape:path-effect']
 
        for pcb_layer in utils.getSurfaceLayers():
 
            # Are there pours in the layer? This makes a difference for whether to place
            # masks
            there_are_pours = utils.checkForPoursInLayer(pcb_layer)

            # Define a group where masks are stored
            mask_group = et.SubElement(self._masks[pcb_layer], 'g')

            # Place defined routes on this SVG layer
            sheet = self._layers[pcb_layer]['copper']['routing']['layer']

            for route_key in (routes.get(pcb_layer) or {}):
                shape_dict = routes[pcb_layer][route_key]
                shape = Shape(shape_dict)
                style = Style(shape_dict, 'copper')
                shape.setStyle(style)

                # Routes are a special case where they are used as-is
                # counting on Inkscapes 'optimised' setting to modify
                # the path such that placement is refleced in
                # it. Therefor we use the original path, not the
                # transformed one as usual
                use_original_path = True
                mirror_path = False
                route_element = place.placeShape(shape,
                                                 sheet,
                                                 mirror_path,
                                                 use_original_path)

                route_element.set('style', shape.getStyleString())

                # Set the key as pcbmode:id of the route. This is used
                # when extracting routing to offset the location of a
                # modified route
                route_element.set('{'+config.cfg['ns']['pcbmode']+'}%s' % ('id'),
                                  route_key)

                # Add a custom buffer definition if it exists
                custom_buffer = shape_dict.get('buffer-to-pour')
                if custom_buffer != None:
                    route_element.set('{'+config.cfg['namespace']['pcbmode']+'}%s' % "buffer-to-pour", str(custom_buffer))

                # TODO: can this be done more elegantly, and "general purpose"?
                for extra_attrib in extra_attributes:
                    ea = shape_dict.get(extra_attrib)
                    if ea is not None:
                        route_element.set('{'+config.cfg['namespace']['inkscape']+'}%s' % (extra_attrib[extra_attrib.index(':')+1:], ea))


                # TODO: this needs to eventually go away or be done properly
                pcbmode_params = shape_dict.get('pcbmode')
                if pcbmode_params is not None:
                    route_element.set('pcbmode', pcbmode_params)                

                if ((there_are_pours == True) and (custom_buffer != "0")):
                    self._placeMask(self._masks[pcb_layer], 
                                    shape, 
                                    'route',
                                    use_original_path)
Example #18
0
    def _placeComponents(self, components, component_type, print_refdef=False):
        """
        """
        for component in components:
            shapes_dict = component.getShapes()
            location = component.getLocation()

            refdef = component.getRefdef()

            if print_refdef == True:
                print refdef,

            # If the component is placed on the bottom layer we need
            # to invert the shapes AND their 'x' coordinate.  This is
            # done using the 'invert' indicator set below
            placement_layer = component.getPlacementLayer()
            if placement_layer == 'bottom':
                invert = True
            else:
                invert = False

            for pcb_layer in utils.getSurfaceLayers():

                there_are_pours = utils.checkForPoursInLayer(pcb_layer)

                # Copper
                shapes = shapes_dict['copper'][pcb_layer]

                if len(shapes) > 0:

                    svg_layer = self._layers[pcb_layer]['copper']['pads']['layer']
     
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])

                    group = et.SubElement(svg_layer, 'g', 
                                          transform=transform)

                    if component_type == 'components':
                        group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', component.getRefdef())
                    elif component_type == 'vias':
                        group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'via')
                        group.set('{'+config.cfg['ns']['pcbmode']+'}via', component.getFootprintName())
                    else:
                        pass

                    for shape in shapes:
                        place.placeShape(shape, group, invert)
                        if there_are_pours == True:
                            mask_group = et.SubElement(self._masks[pcb_layer], 'g', 
                                                       transform=transform)
                            self._placeMask(mask_group, 
                                            shape,
                                            'pad',
                                            original=False,
                                            mirror=invert)

                    # Add pin labels 

                    # There's a bit of a hack here that won't work in
                    # all possible cases (where pads are placed on
                    # bottom layer in a component that has pins placed
                    # both on the top and on the bottom -- a rare
                    # case). Good enough for now

                    labels = shapes_dict['pin-labels']['top']#[pcb_layer]
                    if labels != []:
                        style = utils.dictToStyleText(config.stl['layout']['board']['pad-labels'])
                        label_group = et.SubElement(group, 'g', 
                                                    transform="rotate(%s)" % (((1,-1)[invert])*component.getRotation()),
                                                    style=style)
                        for label in labels:
                            t = et.SubElement(label_group, 'text',
                                              x=str(((1,-1)[invert])*label['location'][0]),
                                              y=str(config.cfg['invert-y']*label['location'][1]))
                            t.text = label['text']





                # Soldermask
                shapes = shapes_dict['soldermask'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['soldermask']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)
     
                    # Solderpaste
                    shapes = shapes_dict['solderpaste'][pcb_layer]
                    svg_layer = self._layers[pcb_layer]['solderpaste']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)


                # Silkscreen
                shapes = shapes_dict['silkscreen'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['silkscreen']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    shape_group = et.SubElement(svg_layer, 'g', transform=transform)
                    shape_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
     
                    for shape in shapes:
                        # Refdefs need to be in their own groups so that their
                        # location can later be extracted, hence this...
                        try:
                            is_refdef = getattr(shape, 'is_refdef')
                        except:
                            is_refdef = False

                        if is_refdef == True:
                            refdef_group = et.SubElement(svg_layer, 'g', transform=transform)
                            refdef_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'refdef')
                            refdef_group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', refdef)
                            placed_element = place.placeShape(shape, refdef_group, invert)
                        else:
                            placed_element = place.placeShape(shape, shape_group, invert)


                # Assembly
                shapes = shapes_dict['assembly'][pcb_layer]
                if len(shapes) > 0: 
                    svg_layer = self._layers[pcb_layer]['assembly']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)

                # Drills
                shapes = shapes_dict['drills'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers['drills']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)
                        placed_element.set('{'+config.cfg['ns']['pcbmode']+'}diameter',
                                           str(shape.getDiameter()))
Example #19
0
    def _placeComponents(self, components, component_type, print_refdef=False):
        """
        """
        for component in components:
            shapes_dict = component.getShapes()
            location = component.getLocation()

            refdef = component.getRefdef()

            if print_refdef == True:
                print refdef,

            # If the component is placed on the bottom layer we need
            # to invert the shapes AND their 'x' coordinate.  This is
            # done using the 'invert' indicator set below
            placement_layer = component.getPlacementLayer()
            if placement_layer == 'bottom':
                invert = True
            else:
                invert = False

            for pcb_layer in utils.getSurfaceLayers():

                there_are_pours = utils.checkForPoursInLayer(pcb_layer)

                # Copper
                shapes = shapes_dict['copper'][pcb_layer]

                if len(shapes) > 0:

                    svg_layer = self._layers[pcb_layer]['copper']['pads'][
                        'layer']

                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])

                    group = et.SubElement(svg_layer, 'g', transform=transform)

                    if component_type == 'components':
                        group.set(
                            '{' + config.cfg['ns']['pcbmode'] + '}refdef',
                            component.getRefdef())
                    elif component_type == 'vias':
                        group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                                  'via')
                        group.set('{' + config.cfg['ns']['pcbmode'] + '}via',
                                  component.getFootprintName())
                    else:
                        pass

                    for shape in shapes:
                        place.placeShape(shape, group, invert)
                        if there_are_pours == True:
                            mask_group = et.SubElement(self._masks[pcb_layer],
                                                       'g',
                                                       transform=transform)
                            self._placeMask(mask_group,
                                            shape,
                                            'pad',
                                            original=False,
                                            mirror=invert)

                    # Add pin labels

                    # There's a bit of a hack here that won't work in
                    # all possible cases (where pads are placed on
                    # bottom layer in a component that has pins placed
                    # both on the top and on the bottom -- a rare
                    # case). Good enough for now

                    labels = shapes_dict['pin-labels']['top']  #[pcb_layer]
                    if labels != []:
                        style = utils.dictToStyleText(
                            config.stl['layout']['board']['pad-labels'])
                        label_group = et.SubElement(
                            group,
                            'g',
                            transform="rotate(%s)" %
                            (((1, -1)[invert]) * component.getRotation()),
                            style=style)
                        for label in labels:
                            t = et.SubElement(label_group,
                                              'text',
                                              x=str(((1, -1)[invert]) *
                                                    label['location'][0]),
                                              y=str(config.cfg['invert-y'] *
                                                    label['location'][1]))
                            t.text = label['text']

                # Soldermask
                shapes = shapes_dict['soldermask'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['soldermask']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)

                    # Solderpaste
                    shapes = shapes_dict['solderpaste'][pcb_layer]
                    svg_layer = self._layers[pcb_layer]['solderpaste']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)

                # Silkscreen
                shapes = shapes_dict['silkscreen'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['silkscreen']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    shape_group = et.SubElement(svg_layer,
                                                'g',
                                                transform=transform)
                    shape_group.set(
                        '{' + config.cfg['ns']['pcbmode'] + '}type',
                        'component-shapes')

                    for shape in shapes:
                        # Refdefs need to be in their own groups so that their
                        # location can later be extracted, hence this...
                        try:
                            is_refdef = getattr(shape, 'is_refdef')
                        except:
                            is_refdef = False

                        if is_refdef == True:
                            refdef_group = et.SubElement(svg_layer,
                                                         'g',
                                                         transform=transform)
                            refdef_group.set(
                                '{' + config.cfg['ns']['pcbmode'] + '}type',
                                'refdef')
                            refdef_group.set(
                                '{' + config.cfg['ns']['pcbmode'] + '}refdef',
                                refdef)
                            placed_element = place.placeShape(
                                shape, refdef_group, invert)
                        else:
                            placed_element = place.placeShape(
                                shape, shape_group, invert)

                # Assembly
                shapes = shapes_dict['assembly'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['assembly']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)

                # Drills
                shapes = shapes_dict['drills'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers['drills']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)
                        placed_element.set(
                            '{' + config.cfg['ns']['pcbmode'] + '}diameter',
                            str(shape.getDiameter()))
Example #20
0
    def __init__(self, refdef, component):
        """
        """

        self._refdef = refdef
        self._layer = component.get('layer') or 'top'

        self._rotate = component.get('rotate') or 0
        if self._layer=='bottom':
            self._rotate *= -1

        self._rotate_point = utils.toPoint(component.get('rotate-point') or [0, 0])
        self._scale = component.get('scale') or 1
        self._location = component.get('location') or [0, 0]

        # Get footprint definition and shapes
        try:
            self._footprint_name = component['footprint']
        except:
            msg.error("Cannot find a 'footprint' name for refdef %s." % refdef)

        filename = self._footprint_name + '.json'

        paths = [os.path.join(config.cfg['base-dir'],
                             config.cfg['locations']['shapes'],
                             filename),
                   os.path.join(config.cfg['base-dir'],
                             config.cfg['locations']['components'],
                             filename)]

        footprint_dict = None
        for path in paths:
            if os.path.isfile(path):
                footprint_dict = utils.dictFromJsonFile(path)
                break

        if footprint_dict == None:
            fname_list = ""
            for path in paths:
                fname_list += " %s" % path
            msg.error("Couldn't find shape file. Looked for it here:\n%s" % (fname_list))

        footprint = Footprint(footprint_dict)
        footprint_shapes = footprint.getShapes()

        #------------------------------------------------        
        # Apply component-specific modifiers to footprint
        #------------------------------------------------
        for sheet in ['conductor', 'soldermask', 'solderpaste', 'pours', 'silkscreen', 'assembly', 'drills']:
            for layer in config.stk['layer-names']:
                for shape in footprint_shapes[sheet].get(layer) or []:

                    # In order to apply the rotation we need to adust the location
                    shape.rotateLocation(self._rotate, self._rotate_point)

                    shape.transformPath(scale=self._scale,
                                        rotate=self._rotate,
                                        rotate_point=self._rotate_point,
                                        mirror=shape.getMirrorPlacement(),
                                        add=True)

        #-------------------------------------------------------------- 
        # Remove silkscreen and assembly shapes if instructed 
        #-------------------------------------------------------------- 
        # If the 'show' flag is 'false then remove these items from the
        # shapes dictionary 
        #--------------------------------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                shapes_dict = component[sheet].get('shapes') or {}
            except:
                shapes_dict = {}

            # If the setting is to not show silkscreen shapes for the
            # component, delete the shapes from the shapes' dictionary
            if shapes_dict.get('show') == False:
                for pcb_layer in utils.getSurfaceLayers():
                    footprint_shapes[sheet][pcb_layer] = []



        #----------------------------------------------------------
        # Add silkscreen and assembly reference designator (refdef)
        #----------------------------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                refdef_dict = component[sheet].get('refdef') or {}
            except:
                refdef_dict = {}
     
            if refdef_dict.get('show') != False:
                layer = refdef_dict.get('layer') or 'top'
         
                # Rotate the refdef; if unspecified the rotation is the same as
                # the rotation of the component
                refdef_dict['rotate'] = refdef_dict.get('rotate') or 0
 
                # Sometimes you'd want to keep all refdefs at the same angle
                # and not rotated with the component
                if refdef_dict.get('rotate-with-component') != False:
                    refdef_dict['rotate'] += self._rotate

                refdef_dict['rotate-point'] = utils.toPoint(refdef_dict.get('rotate-point')) or self._rotate_point
     
                refdef_dict['location'] = refdef_dict.get('location') or [0, 0]
                refdef_dict['type'] = 'text'
                refdef_dict['value'] = refdef_dict.get('value') or refdef
                refdef_dict['font-family'] = (refdef_dict.get('font-family') or
                                              config.stl['layout'][sheet]['refdef'].get('font-family') or 
                                              config.stl['defaults']['font-family'])
                refdef_dict['font-size'] = (refdef_dict.get('font-size') or 
                                            config.stl['layout'][sheet]['refdef'].get('font-size') or 
                                            "2mm")
                refdef_shape = Shape(refdef_dict)

                refdef_shape.is_refdef = True
                refdef_shape.rotateLocation(self._rotate, self._rotate_point)
                style = Style(refdef_dict, sheet, 'refdef')
                refdef_shape.setStyle(style)

                # Add the refdef to the silkscreen/assembly list. It's
                # important that this is added at the very end since the
                # placement process assumes the refdef is last
                try:
                    footprint_shapes[sheet][layer]
                except:
                    footprint_shapes[sheet][layer] = []

                footprint_shapes[sheet][layer].append(refdef_shape)
                    

        #------------------------------------------------------
        # Invert layers
        #------------------------------------------------------
        # If the placement is on the bottom of the baord then we need
        # to invert the placement of all components. This affects the
        # surface laters but also internal layers

        if self._layer == 'bottom':
            layers = config.stk['layer-names']
           
            for sheet in ['conductor', 'pours', 'soldermask', 'solderpaste', 'silkscreen', 'assembly']:
                sheet_dict = footprint_shapes[sheet]
                sheet_dict_new = {}
                for i, pcb_layer in enumerate(layers):
                    try:
                        sheet_dict_new[layers[len(layers)-i-1]] = copy.copy(sheet_dict[pcb_layer])
                    except:
                        continue

                footprint_shapes[sheet] = copy.copy(sheet_dict_new)    

        self._footprint_shapes = footprint_shapes
Example #21
0
    def __init__(self, refdef, component):
        """
        """

        self._refdef = refdef
        self._layer = component.get('layer') or 'top'

        self._rotate = component.get('rotate') or 0
        self._rotate_point = utils.toPoint(component.get('rotate-point') or [0, 0])
        self._scale = component.get('scale') or 1
        self._location = component.get('location') or [0, 0]

        # Get footprint definition and shapes
        try:
            self._footprint_name = component['footprint']
        except:
            msg.error("Cannot find a 'footprint' name for refdef %s." % refdef)

        fname = os.path.join(config.cfg['base-dir'],
                             config.cfg['locations']['components'],
                             self._footprint_name + '.json')
        footprint_dict = utils.dictFromJsonFile(fname)
        footprint = Footprint(footprint_dict)
        footprint_shapes = footprint.getShapes()

        #------------------------------------------------        
        # Apply component-specific modifiers to footprint
        #------------------------------------------------
        for sheet in ['copper', 'soldermask', 'solderpaste', 'silkscreen', 'assembly', 'drills']:
            for layer in utils.getSurfaceLayers() + utils.getInternalLayers():
                for shape in footprint_shapes[sheet][layer]:
                    # In order to apply the rotation we need to adust the location
                    # of each element
                    shape.rotateLocation(self._rotate, self._rotate_point)

                    shape.transformPath(self._scale,
                                        self._rotate,
                                        self._rotate_point,
                                        False,
                                        True)

        #--------------------------------------
        # Remove silkscreen and assembly shapes
        #--------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                shapes_dict = component[sheet].get('shapes') or {}
            except:
                shapes_dict = {}

            # If the setting is to not show silkscreen shapes for the
            # component, delete the shapes from the shapes' dictionary
            if shapes_dict.get('show') == False:
                for pcb_layer in utils.getSurfaceLayers():
                    footprint_shapes[sheet][pcb_layer] = []



        #----------------------------------------------------------
        # Add silkscreen and assembly reference designator (refdef)
        #----------------------------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                refdef_dict = component[sheet].get('refdef') or {}
            except:
                refdef_dict = {}
     
            if refdef_dict.get('show') != False:
                layer = refdef_dict.get('layer') or 'top'
         
                # Rotate the refdef; if unspecified the rotation is the same as
                # the rotation of the component
                refdef_dict['rotate'] = refdef_dict.get('rotate') or 0
 
                # Sometimes you'd want to keep all refdefs at the same angle
                # and not rotated with the component
                if refdef_dict.get('rotate-with-component') != False:
                    refdef_dict['rotate'] += self._rotate

                refdef_dict['rotate-point'] = utils.toPoint(refdef_dict.get('rotate-point')) or self._rotate_point
     
                refdef_dict['location'] = refdef_dict.get('location') or [0, 0]
                refdef_dict['type'] = 'text'
                refdef_dict['value'] = refdef_dict.get('value') or refdef
                refdef_dict['font-family'] = (refdef_dict.get('font-family') or
                                              config.stl['layout'][sheet]['refdef'].get('font-family') or 
                                              config.stl['defaults']['font-family'])
                refdef_dict['font-size'] = (refdef_dict.get('font-size') or 
                                            config.stl['layout'][sheet]['refdef'].get('font-size') or 
                                            "2mm")
                refdef_shape = Shape(refdef_dict)

                refdef_shape.is_refdef = True
                refdef_shape.rotateLocation(self._rotate, self._rotate_point)
                style = Style(refdef_dict, sheet, 'refdef')
                refdef_shape.setStyle(style)

                # Add the refdef to the silkscreen/assembly list. It's
                # important that this is added at the very end since the
                # placement process assumes the refdef is last
                footprint_shapes[sheet][layer].append(refdef_shape)


        #------------------------------------------------------
        # Invert 'top' and 'bottom' if layer is on the 'bottom'
        #------------------------------------------------------
        if self._layer == 'bottom':
            layers = utils.getSurfaceLayers()
            layers_reversed = reversed(utils.getSurfaceLayers())
           
            for sheet in ['copper', 'soldermask', 'solderpaste', 'silkscreen', 'assembly']:
                sheet_dict = footprint_shapes[sheet]
                # TODO: this nasty hack won't work for more than two
                # layers, so when 2+ are supported this needs to be
                # revisited
                for i in range(0,len(layers)-1):
                    sheet_dict['temp'] = sheet_dict.pop(layers[i])
                    sheet_dict[layers[i]] = sheet_dict.pop(layers[len(layers)-1-i])
                    sheet_dict[layers[len(layers)-1-i]] = sheet_dict.pop('temp')

        self._footprint_shapes = footprint_shapes
Example #22
0
    def __init__(self, refdef, component):
        """
        """

        self._refdef = refdef
        self._layer = component.get('layer') or 'top'

        self._rotate = component.get('rotate') or 0
        self._rotate_point = utils.toPoint(component.get('rotate-point') or [0, 0])
        self._scale = component.get('scale') or 1
        self._location = component.get('location') or [0, 0]

        # Get footprint definition and shapes
        try:
            self._footprint_name = component['footprint']
        except:
            msg.error("Cannot find a 'footprint' name for refdef %s." % refdef)

        fname = os.path.join(config.cfg['base-dir'],
                             config.cfg['locations']['components'],
                             self._footprint_name + '.json')
        footprint_dict = utils.dictFromJsonFile(fname)
        footprint = Footprint(footprint_dict)
        footprint_shapes = footprint.getShapes()

        #------------------------------------------------        
        # Apply component-specific modifiers to footprint
        #------------------------------------------------
        for sheet in ['copper', 'soldermask', 'solderpaste', 'silkscreen', 'assembly', 'drills']:
            for layer in utils.getSurfaceLayers() + utils.getInternalLayers():
                for shape in footprint_shapes[sheet][layer]:
                    # In order to apply the rotation we need to adust the location
                    # of each element
                    shape.rotateLocation(self._rotate, self._rotate_point)

                    shape.transformPath(self._scale,
                                        self._rotate,
                                        self._rotate_point,
                                        False,
                                        True)


        #--------------------------------------
        # Remove silkscreen and assembly shapes
        #--------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                shapes_dict = component[sheet].get('shapes') or {}
            except:
                shapes_dict = {}

            # If the setting is to not show silkscreen shapes for the
            # component, delete the shapes from the shapes' dictionary
            if shapes_dict.get('show') == False:
                for pcb_layer in utils.getSurfaceLayers():
                    footprint_shapes[sheet][pcb_layer] = []



        #----------------------------------------------------------
        # Add silkscreen and assembly reference designator (refdef)
        #----------------------------------------------------------
        for sheet in ['silkscreen','assembly']:
            
            try:
                refdef_dict = component[sheet].get('refdef') or {}
            except:
                refdef_dict = {}
     
            if refdef_dict.get('show') != False:
                layer = refdef_dict.get('layer') or 'top'
         
                # Rotate the refdef; if unspecified the rotation is the same as
                # the rotation of the component
                refdef_dict['rotate'] = refdef_dict.get('rotate') or 0
 
                # Sometimes you'd want to keep all refdefs at the same angle
                # and not rotated with the component
                if refdef_dict.get('rotate-with-component') != False:
                    refdef_dict['rotate'] += self._rotate

                refdef_dict['rotate-point'] = utils.toPoint(refdef_dict.get('rotate-point')) or self._rotate_point
     
                refdef_dict['location'] = refdef_dict.get('location') or [0, 0]
                refdef_dict['type'] = 'text'
                refdef_dict['value'] = refdef
                refdef_dict['font-family'] = (config.stl['layout'][sheet]['refdef'].get('font-family') or 
                                              config.stl['defaults']['font-family'])
                refdef_dict['font-size'] = (config.stl['layout'][sheet]['refdef'].get('font-size') or 
                                              "2mm")
                refdef_shape = Shape(refdef_dict)
                refdef_shape.is_refdef = True
                refdef_shape.rotateLocation(self._rotate, self._rotate_point)
                style = Style(refdef_dict, sheet, 'refdef')
                refdef_shape.setStyle(style)

                # Add the refdef to the silkscreen/assembly list. It's
                # important that this is added at the very end since the
                # plcament process assumes the refdef is last
                footprint_shapes[sheet][layer].append(refdef_shape)


        #------------------------------------------------------
        # Invert 'top' and 'bottom' if layer is on the 'bottom'
        #------------------------------------------------------
        if self._layer == 'bottom':
            layers = utils.getSurfaceLayers()
            layers_reversed = reversed(utils.getSurfaceLayers())
           
            for sheet in ['copper', 'soldermask', 'solderpaste', 'silkscreen', 'assembly']:
                sheet_dict = footprint_shapes[sheet]
                # TODO: this nasty hack won't work for more than two
                # layers, so when 2+ are supported this needs to be
                # revisited
                for i in range(0,len(layers)-1):
                    sheet_dict['temp'] = sheet_dict.pop(layers[i])
                    sheet_dict[layers[i]] = sheet_dict.pop(layers[len(layers)-1-i])
                    sheet_dict[layers[len(layers)-1-i]] = sheet_dict.pop('temp')

        self._footprint_shapes = footprint_shapes
Example #23
0
def gerberise(manufacturer='default'):
    """
    Generate Gerbers for one or more layers
    """

    # Open the board's SVG
    svg_in = utils.openBoardSVG()

    # Get Gerber generation settings
    gcd = config.brd['gerber']
    decimals = gcd['decimals']
    digits = gcd['digits']
    steps = gcd['steps-per-segment']
    length = gcd['min-segment-length']

    # Get layer data
    xpath_regex = ""
    ns = {
        'pcbmode': config.cfg['ns']['pcbmode'],
        'svg': config.cfg['ns']['svg']
    }

    # Save to file
    base_dir = os.path.join(config.cfg['base-dir'],
                            config.cfg['locations']['build'], 'production')
    # Create directory if it doesn't exist already
    utils.create_dir(base_dir)
    base_name = "%s_rev_%s" % (config.brd['config']['name'],
                               config.brd['config']['rev'])

    filename_info = config.cfg['manufacturers'][manufacturer]['filenames'][
        'gerbers']

    # Process Gerbers for PCB layers and sheets
    for pcb_layer in utils.getSurfaceLayers():

        # Get the SVG layer corresponding to the PCB layer
        svg_layer = svg_in.find("//svg:g[@pcbmode:pcb-layer='%s']" %
                                (pcb_layer),
                                namespaces=ns)

        # Get masks (must be placed right after pours)
        mask_paths = svg_in.findall(
            ".//svg:defs//svg:mask[@pcbmode:pcb-layer='%s']//svg:path" %
            pcb_layer,
            namespaces=ns)

        sheets = ['copper', 'soldermask', 'solderpaste', 'silkscreen']
        for sheet in sheets:
            # Get the SVG layer corresponding to the 'sheet'
            sheet_layer = svg_layer.find(".//svg:g[@pcbmode:sheet='%s']" %
                                         (sheet),
                                         namespaces=ns)

            if sheet == 'copper':
                mask_paths_to_pass = mask_paths
            else:
                mask_paths_to_pass = []

            # Create a Gerber object
            gerber = Gerber(sheet_layer, mask_paths_to_pass, decimals, digits,
                            steps, length)

            add = '_%s_%s.%s' % (pcb_layer, sheet,
                                 filename_info[pcb_layer][sheet].get('ext')
                                 or 'ger')
            filename = os.path.join(base_dir, base_name + add)

            with open(filename, "wb") as f:
                for line in gerber.getGerber():
                    f.write(line)

    # Process module sheets
    sheets = ['outline', 'documentation']
    for sheet in sheets:
        # Get the SVG layer corresponding to the 'sheet'
        sheet_layer = svg_in.find(".//svg:g[@pcbmode:sheet='%s']" % (sheet),
                                  namespaces=ns)

        # Create a Gerber object
        gerber = Gerber(sheet_layer, [], decimals, digits, steps, length)

        add = '_%s.%s' % (sheet, filename_info['other'][sheet].get('ext')
                          or 'ger')
        filename = os.path.join(base_dir, base_name + add)

        with open(filename, "wb") as f:
            for line in gerber.getGerber(False):
                f.write(line)

    return ['bullshit']
Example #24
0
    def _placeComponents(self, components, component_type, print_refdef=False):
        """
        """
        for component in components:
            shapes_dict = component.getShapes()
            location = component.getLocation()

            # If the component is placed on the bottom layer we need
            # to invert the shapes AND their 'x' coordinate.  This is
            # sone using the 'invert' indicator set below
            refdef = component.getRefdef()

            if print_refdef == True:
                print refdef,

            placement_layer = component.getPlacementLayer()
            if placement_layer == 'bottom':
                invert = True
            else:
                invert = False

            for pcb_layer in utils.getSurfaceLayers():

                there_are_pours = utils.checkForPoursInLayer(pcb_layer)

                # Copper
                shapes = shapes_dict['copper'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['copper']['pads'][
                        'layer']

                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)

                    if component_type == 'components':
                        group.set(
                            '{' + config.cfg['ns']['pcbmode'] + '}refdef',
                            component.getRefdef())
                    elif component_type == 'vias':
                        group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                                  'via')
                        group.set('{' + config.cfg['ns']['pcbmode'] + '}via',
                                  component.getFootprintName())
                    else:
                        pass

                    for shape in shapes:
                        place.placeShape(shape, group)
                        if there_are_pours == True:
                            mask_group = et.SubElement(self._masks[pcb_layer],
                                                       'g',
                                                       transform=transform)
                            self._placeMask(mask_group, shape, 'pad')

                    # Add pin labels
                    labels = shapes_dict['pin-labels'][pcb_layer]
                    if labels != []:
                        style = utils.dictToStyleText(
                            config.stl['layout']['board']['pad-labels'])
                        label_group = et.SubElement(group,
                                                    'g',
                                                    transform="rotate(%s)" %
                                                    component.getRotation(),
                                                    style=style)
                        for label in labels:
                            t = et.SubElement(
                                label_group,
                                'text',
                                x=str(label['location'][0]),
                                # TODO: get rid of this hack
                                y=str(-label['location'][1]))
                            #y=str(-pin_location.y + pad_numbers_font_size/3),
                            #refdef=self._refdef)
                            t.text = label['text']

                # Soldermask
                shapes = shapes_dict['soldermask'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['soldermask']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)

                    # Solderpaste
                    shapes = shapes_dict['solderpaste'][pcb_layer]
                    svg_layer = self._layers[pcb_layer]['solderpaste']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)

                # Silkscreen
                shapes = shapes_dict['silkscreen'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['silkscreen']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    shape_group = et.SubElement(svg_layer,
                                                'g',
                                                transform=transform)
                    shape_group.set(
                        '{' + config.cfg['ns']['pcbmode'] + '}type',
                        'component-shapes')

                    for shape in shapes:
                        # Refdefs need to be in their own groups so that their
                        # location can later be extracted, hence this...
                        try:
                            is_refdef = getattr(shape, 'is_refdef')
                        except:
                            is_refdef = False

                        if is_refdef == True:
                            refdef_group = et.SubElement(svg_layer,
                                                         'g',
                                                         transform=transform)
                            refdef_group.set(
                                '{' + config.cfg['ns']['pcbmode'] + '}type',
                                'refdef')
                            refdef_group.set(
                                '{' + config.cfg['ns']['pcbmode'] + '}refdef',
                                refdef)
                            placed_element = place.placeShape(
                                shape, refdef_group)
                        else:
                            placed_element = place.placeShape(
                                shape, shape_group)

                # Assembly
                shapes = shapes_dict['assembly'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['assembly']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)

                # Drills
                shapes = shapes_dict['drills'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers['drills']['layer']
                    transform = "translate(%s,%s)" % (
                        location[0], config.cfg['invert-y'] * location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                              'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)
                        placed_element.set(
                            '{' + config.cfg['ns']['pcbmode'] + '}diameter',
                            str(shape.getDiameter()))
Example #25
0
def extractRouting(svg_in):
    """
    Extracts routing from the the 'routing' SVG layers of each PCB layer.
    Inkscape SVG layers for each PCB ('top', 'bottom', etc.) layer.
    """

    # Open the routing file if it exists. The existing data is used
    # for stats displayed as PCBmodE is run. The file is then
    # overwritten.
    output_file = os.path.join(config.cfg['base-dir'],
                               config.cfg['name'] + '_routing.json')
    try:
        routing_dict_old = utils.dictFromJsonFile(output_file, False)
    except:
        routing_dict_old = {'routes': {}} 

    #---------------
    # Extract routes
    #---------------

    # Store extracted data here
    routing_dict = {}

    # The XPATH expression for extracting routes, but not vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='routing']//svg:path[(@d) and not (@pcbmode:type='via')]"

    routes_dict = {'top': {}, 'bottom': {}}

    for pcb_layer in utils.getSurfaceLayers():
        routes = svg_in.xpath(xpath_expr % pcb_layer, 
                              namespaces={'pcbmode':config.cfg['ns']['pcbmode'], 
                                          'svg':config.cfg['ns']['svg']})
        for route in routes:
            route_dict = {}
            route_id = route.get('{'+config.cfg['ns']['pcbmode']+'}id')
            path = route.get('d')

            style_text = route.get('style') or ''
            
            # This hash digest provides a unique identifier for
            # the route based on its path, location, and style
            digest = utils.digest(path+
                                  #str(location.x)+
                                  #str(location.y)+
                                  style_text)

            routes_dict[pcb_layer][digest] = {}
            routes_dict[pcb_layer][digest]['type'] = 'path'
            routes_dict[pcb_layer][digest]['value'] = path

            stroke_width = utils.getStyleAttrib(style_text, 'stroke-width')
            if stroke_width != None:
                routes_dict[pcb_layer][digest]['style'] = 'stroke'
                routes_dict[pcb_layer][digest]['stroke-width'] = round(float(stroke_width), 4)

            custom_buffer = route.get('{'+config.cfg['ns']['pcbmode']+'}buffer-to-pour')
            if custom_buffer != None:
                routes_dict[pcb_layer][digest]['buffer-to-pour'] = float(custom_buffer)

            gerber_lp = route.get('{'+config.cfg['ns']['pcbmode']+'}gerber-lp')
            if gerber_lp != None:
                routes_dict[pcb_layer][digest]['gerber-lp'] = gerber_lp



    routing_dict['routes'] = routes_dict

    # Create simple stats and display them
    total = 0
    total_old = 0
    new = 0
    existing = 0
    for pcb_layer in utils.getSurfaceLayers():
        try:
            total += len(routing_dict['routes'][pcb_layer])
        except:
            pass
        try:
            new_dict = routing_dict['routes'][pcb_layer]
        except:
            new_dict = {}
        try:
            old_dict = routing_dict_old['routes'][pcb_layer]
        except:
            old_dict = {}
        for key in new_dict:
            if key not in old_dict:
                new += 1
            else:
                existing += 1

    for pcb_layer in utils.getSurfaceLayers():
        total_old += len(old_dict)

    message = "Extracted %s routes; %s new (or modified), %s existing" % (total, new, existing)
    if total_old > total:
        message += ", %s removed" % (total_old - total)
    msg.subInfo(message)

    #-------------
    # Extract vias
    #-------------

    # XPATH expression for extracting vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='routing']//svg:path[@pcbmode:type='via']"
    # Get new vias; only search the top layer
    new_vias = svg_in.xpath(xpath_expr % 'top', 
                            namespaces={'pcbmode':config.cfg['ns']['pcbmode'], 
                                        'svg':config.cfg['ns']['svg']})    


    # XPATH expression for extracting vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='pads']//svg:g[@pcbmode:type='via']"
    # Get new vias; only search the top layer
    vias = svg_in.xpath(xpath_expr % 'top', 
                        namespaces={'pcbmode':config.cfg['ns']['pcbmode'], 
                                    'svg':config.cfg['ns']['svg']})    

    vias_dict = {}


    for via in vias:

        transform = via.get('transform')
        if transform != None:
            transform_data = utils.parseTransform(transform)
            location = transform_data['location']
        else:
            location = Point()

        # Invery 'y' axis if needed
        location.y *= config.cfg['invert-y']

        digest = utils.digest("%s%s" % (location.x, location.y))

        # Define a via, just like any other component, but disable
        # placement of refdef
        vias_dict[digest] = {}
        vias_dict[digest]['footprint'] = via.get('{'+config.cfg['ns']['pcbmode']+'}via')
        vias_dict[digest]['location'] = [location.x, location.y]
        vias_dict[digest]['silkscreen'] = {'refdef': {'show': False }}
        vias_dict[digest]['assembly'] = {'refdef': {'show': False }}
        vias_dict[digest]['layer'] = 'top'

    

    for via in new_vias:

        # A newly-defined via will have a location set through the
        # 'sodipodi' namespace and possible also through a transform
        try:
            sodipodi_loc = Point(via.get('{'+config.cfg['ns']['sodipodi']+'}cx'), 
                            via.get('{'+config.cfg['ns']['sodipodi']+'}cy'))
        except:
            sodipodi_loc = Pound()

        transform = via.get('transform')
        if transform != None:
            transform_data = utils.parseTransform(transform)
            location = transform_data['location']
        else:
            location = Point()

        location += sodipodi_loc

        # Invery 'y' axis if needed
        location.y *= config.cfg['invert-y']

        digest = utils.digest("%s%s" % (location.x, location.y))

        # Define a via, just like any other component, but disable
        # placement of refdef
        vias_dict[digest] = {}
        vias_dict[digest]['footprint'] = via.get('{'+config.cfg['ns']['pcbmode']+'}via')
        vias_dict[digest]['location'] = [location.x, location.y]
        vias_dict[digest]['silkscreen'] = {'refdef': {'show': False }}
        vias_dict[digest]['assembly'] = {'refdef': {'show': False }}
        vias_dict[digest]['layer'] = 'top'






    routing_dict['vias'] = vias_dict

    # Display stats
    if len(vias_dict) == 0:
        msg.subInfo("No vias found")
    elif len(vias_dict) == 1:
        msg.subInfo("Extracted 1 via")
    else:
        msg.subInfo("Extracted %s vias" % (len(vias_dict)))
    

    # Save extracted routing into routing file
    try:
        with open(output_file, 'wb') as f:
            f.write(json.dumps(routing_dict, sort_keys=True, indent=2))
    except:
        msg.error("Cannot save file %s" % output_file)

    return
Example #26
0
def extractRouting(svg_in):
    """
    Extracts routing from the the 'routing' SVG layers of each PCB layer.
    Inkscape SVG layers for each PCB ('top', 'bottom', etc.) layer.
    """

    # Open the routing file if it exists. The existing data is used
    # for stats displayed as PCBmodE is run. The file is then
    # overwritten.
    output_file = os.path.join(config.cfg['base-dir'],
                               config.cfg['name'] + '_routing.json')
    try:
        routing_dict_old = utils.dictFromJsonFile(output_file, False)
    except:
        routing_dict_old = {'routes': {}}

    #---------------
    # Extract routes
    #---------------

    # Store extracted data here
    routing_dict = {}

    # The XPATH expression for extracting routes, but not vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='routing']//svg:path[(@d) and not (@pcbmode:type='via')]"

    routes_dict = {'top': {}, 'bottom': {}}

    for pcb_layer in utils.getSurfaceLayers():
        routes = svg_in.xpath(xpath_expr % pcb_layer,
                              namespaces={
                                  'pcbmode': config.cfg['ns']['pcbmode'],
                                  'svg': config.cfg['ns']['svg']
                              })
        for route in routes:
            route_dict = {}
            route_id = route.get('{' + config.cfg['ns']['pcbmode'] + '}id')
            path = route.get('d')

            style_text = route.get('style') or ''

            # This hash digest provides a unique identifier for
            # the route based on its path, location, and style
            digest = utils.digest(path +
                                  #str(location.x)+
                                  #str(location.y)+
                                  style_text)

            routes_dict[pcb_layer][digest] = {}
            routes_dict[pcb_layer][digest]['type'] = 'path'
            routes_dict[pcb_layer][digest]['value'] = path

            stroke_width = utils.getStyleAttrib(style_text, 'stroke-width')
            if stroke_width != None:
                # Sometimes Inkscape will add a 'px' suffix to the stroke-width
                #property pf a path; this removes it
                stroke_width = stroke_width.rstrip('px')
                routes_dict[pcb_layer][digest]['style'] = 'stroke'
                routes_dict[pcb_layer][digest]['stroke-width'] = round(
                    float(stroke_width), 4)

            custom_buffer = route.get('{' + config.cfg['ns']['pcbmode'] +
                                      '}buffer-to-pour')
            if custom_buffer != None:
                routes_dict[pcb_layer][digest]['buffer-to-pour'] = float(
                    custom_buffer)

            gerber_lp = route.get('{' + config.cfg['ns']['pcbmode'] +
                                  '}gerber-lp')
            if gerber_lp != None:
                routes_dict[pcb_layer][digest]['gerber-lp'] = gerber_lp

    routing_dict['routes'] = routes_dict

    # Create simple stats and display them
    total = 0
    total_old = 0
    new = 0
    existing = 0
    for pcb_layer in utils.getSurfaceLayers():
        try:
            total += len(routing_dict['routes'][pcb_layer])
        except:
            pass
        try:
            new_dict = routing_dict['routes'][pcb_layer]
        except:
            new_dict = {}
        try:
            old_dict = routing_dict_old['routes'][pcb_layer]
        except:
            old_dict = {}
        for key in new_dict:
            if key not in old_dict:
                new += 1
            else:
                existing += 1

    for pcb_layer in utils.getSurfaceLayers():
        total_old += len(old_dict)

    message = "Extracted %s routes; %s new (or modified), %s existing" % (
        total, new, existing)
    if total_old > total:
        message += ", %s removed" % (total_old - total)
    msg.subInfo(message)

    #-------------
    # Extract vias
    #-------------

    # XPATH expression for extracting vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='routing']//svg:*[@pcbmode:type='via']"
    # Get new vias; only search the top layer
    new_vias = svg_in.xpath(xpath_expr % 'top',
                            namespaces={
                                'pcbmode': config.cfg['ns']['pcbmode'],
                                'svg': config.cfg['ns']['svg']
                            })

    # XPATH expression for extracting vias
    xpath_expr = "//svg:g[@pcbmode:pcb-layer='%s']//svg:g[@pcbmode:sheet='pads']//svg:g[@pcbmode:type='via']"
    # Get nexisting vias; only search the top layer
    vias = svg_in.xpath(xpath_expr % 'top',
                        namespaces={
                            'pcbmode': config.cfg['ns']['pcbmode'],
                            'svg': config.cfg['ns']['svg']
                        })

    vias_dict = {}

    for via in vias:

        transform = via.get('transform')
        if transform != None:
            transform_data = utils.parseTransform(transform)
            location = transform_data['location']
        else:
            location = Point()

        # Invery 'y' axis if needed
        location.y *= config.cfg['invert-y']

        digest = utils.digest("%s%s" % (location.x, location.y))

        # Define a via, just like any other component, but disable
        # placement of refdef
        vias_dict[digest] = {}
        vias_dict[digest]['footprint'] = via.get('{' +
                                                 config.cfg['ns']['pcbmode'] +
                                                 '}via')
        vias_dict[digest]['location'] = [location.x, location.y]
        vias_dict[digest]['silkscreen'] = {'refdef': {'show': False}}
        vias_dict[digest]['assembly'] = {'refdef': {'show': False}}
        vias_dict[digest]['layer'] = 'top'

    for via in new_vias:

        # A newly-defined via will have a location set through the
        # 'sodipodi' namespace and possible also through a transform
        try:
            # The commented lines below wored fro Inkscape prior to 0.91
            #sodipodi_loc = Point(via.get('{'+config.cfg['ns']['sodipodi']+'}cx'),
            #                via.get('{'+config.cfg['ns']['sodipodi']+'}cy'))
            sodipodi_loc = Point(via.get('cx'), via.get('cy'))
        except:
            sodipodi_loc = Point()

        print sodipodi_loc

        transform = via.get('transform')
        if transform != None:
            transform_data = utils.parseTransform(transform)
            location = transform_data['location']
        else:
            location = Point()

        location += sodipodi_loc

        # Invery 'y' axis if needed
        location.y *= config.cfg['invert-y']

        digest = utils.digest("%s%s" % (location.x, location.y))

        # Define a via, just like any other component, but disable
        # placement of refdef
        vias_dict[digest] = {}
        vias_dict[digest]['footprint'] = via.get('{' +
                                                 config.cfg['ns']['pcbmode'] +
                                                 '}via')
        vias_dict[digest]['location'] = [location.x, location.y]
        vias_dict[digest]['silkscreen'] = {'refdef': {'show': False}}
        vias_dict[digest]['assembly'] = {'refdef': {'show': False}}
        vias_dict[digest]['layer'] = 'top'

    routing_dict['vias'] = vias_dict

    # Display stats
    if len(vias_dict) == 0:
        msg.subInfo("No vias found")
    elif len(vias_dict) == 1:
        msg.subInfo("Extracted 1 via")
    else:
        msg.subInfo("Extracted %s vias" % (len(vias_dict)))

    # Save extracted routing into routing file
    try:
        with open(output_file, 'wb') as f:
            f.write(json.dumps(routing_dict, sort_keys=True, indent=2))
    except:
        msg.error("Cannot save file %s" % output_file)

    return
Example #27
0
    def __init__(self, refdef, component):
        """
        """

        self._refdef = refdef
        self._layer = component.get('layer') or 'top'

        self._rotate = component.get('rotate') or 0
        if self._layer == 'bottom':
            self._rotate *= -1

        self._rotate_point = utils.toPoint(
            component.get('rotate-point') or [0, 0])
        self._scale = component.get('scale') or 1
        self._location = component.get('location') or [0, 0]

        # Get footprint definition and shapes
        try:
            self._footprint_name = component['footprint']
        except:
            msg.error("Cannot find a 'footprint' name for refdef %s." % refdef)

        filename = self._footprint_name + '.json'

        paths = [
            os.path.join(config.cfg['base-dir'],
                         config.cfg['locations']['shapes'], filename),
            os.path.join(config.cfg['base-dir'],
                         config.cfg['locations']['components'], filename)
        ]

        footprint_dict = None
        for path in paths:
            if os.path.isfile(path):
                footprint_dict = utils.dictFromJsonFile(path)
                break

        if footprint_dict == None:
            fname_list = ""
            for path in paths:
                fname_list += " %s" % path
            msg.error("Couldn't find shape file. Looked for it here:\n%s" %
                      (fname_list))

        footprint = Footprint(footprint_dict)
        footprint_shapes = footprint.getShapes()

        #------------------------------------------------
        # Apply component-specific modifiers to footprint
        #------------------------------------------------
        for sheet in [
                'conductor', 'soldermask', 'solderpaste', 'pours',
                'silkscreen', 'assembly', 'drills'
        ]:
            for layer in config.stk['layer-names']:
                for shape in footprint_shapes[sheet].get(layer) or []:

                    # In order to apply the rotation we need to adust the location
                    shape.rotateLocation(self._rotate, self._rotate_point)

                    shape.transformPath(scale=self._scale,
                                        rotate=self._rotate,
                                        rotate_point=self._rotate_point,
                                        mirror=shape.getMirrorPlacement(),
                                        add=True)

        #--------------------------------------------------------------
        # Remove silkscreen and assembly shapes if instructed
        #--------------------------------------------------------------
        # If the 'show' flag is 'false then remove these items from the
        # shapes dictionary
        #--------------------------------------------------------------
        for sheet in ['silkscreen', 'assembly']:

            try:
                shapes_dict = component[sheet].get('shapes') or {}
            except:
                shapes_dict = {}

            # If the setting is to not show silkscreen shapes for the
            # component, delete the shapes from the shapes' dictionary
            if shapes_dict.get('show') == False:
                for pcb_layer in utils.getSurfaceLayers():
                    footprint_shapes[sheet][pcb_layer] = []

        #----------------------------------------------------------
        # Add silkscreen and assembly reference designator (refdef)
        #----------------------------------------------------------
        for sheet in ['silkscreen', 'assembly']:

            try:
                refdef_dict = component[sheet].get('refdef') or {}
            except:
                refdef_dict = {}

            if refdef_dict.get('show') != False:
                layer = refdef_dict.get('layer') or 'top'

                # Rotate the refdef; if unspecified the rotation is the same as
                # the rotation of the component
                refdef_dict['rotate'] = refdef_dict.get('rotate') or 0

                # Sometimes you'd want to keep all refdefs at the same angle
                # and not rotated with the component
                if refdef_dict.get('rotate-with-component') != False:
                    refdef_dict['rotate'] += self._rotate

                refdef_dict['rotate-point'] = utils.toPoint(
                    refdef_dict.get('rotate-point')) or self._rotate_point

                refdef_dict['location'] = refdef_dict.get('location') or [0, 0]
                refdef_dict['type'] = 'text'
                refdef_dict['value'] = refdef_dict.get('value') or refdef
                refdef_dict['font-family'] = (
                    refdef_dict.get('font-family')
                    or config.stl['layout'][sheet]['refdef'].get('font-family')
                    or config.stl['defaults']['font-family'])
                refdef_dict['font-size'] = (
                    refdef_dict.get('font-size')
                    or config.stl['layout'][sheet]['refdef'].get('font-size')
                    or "2mm")
                refdef_shape = Shape(refdef_dict)

                refdef_shape.is_refdef = True
                refdef_shape.rotateLocation(self._rotate, self._rotate_point)
                style = Style(refdef_dict, sheet, 'refdef')
                refdef_shape.setStyle(style)

                # Add the refdef to the silkscreen/assembly list. It's
                # important that this is added at the very end since the
                # placement process assumes the refdef is last
                try:
                    footprint_shapes[sheet][layer]
                except:
                    footprint_shapes[sheet][layer] = []

                footprint_shapes[sheet][layer].append(refdef_shape)

        #------------------------------------------------------
        # Invert layers
        #------------------------------------------------------
        # If the placement is on the bottom of the baord then we need
        # to invert the placement of all components. This affects the
        # surface laters but also internal layers

        if self._layer == 'bottom':
            layers = config.stk['layer-names']

            for sheet in [
                    'conductor', 'pours', 'soldermask', 'solderpaste',
                    'silkscreen', 'assembly'
            ]:
                sheet_dict = footprint_shapes[sheet]
                sheet_dict_new = {}
                for i, pcb_layer in enumerate(layers):
                    try:
                        sheet_dict_new[layers[len(layers) - i -
                                              1]] = copy.copy(
                                                  sheet_dict[pcb_layer])
                    except:
                        continue

                footprint_shapes[sheet] = copy.copy(sheet_dict_new)

        self._footprint_shapes = footprint_shapes
Example #28
0
    def _placeComponents(self, components, component_type, print_refdef=False):
        """
        """
        for component in components:
            shapes_dict = component.getShapes()
            location = component.getLocation()

            # If the component is placed on the bottom layer we need
            # to invert the shapes AND their 'x' coordinate.  This is
            # sone using the 'invert' indicator set below
            refdef = component.getRefdef()

            if print_refdef == True:
                print refdef,

            placement_layer = component.getPlacementLayer()
            if placement_layer == 'bottom':
                invert = True
            else:
                invert = False

            for pcb_layer in utils.getSurfaceLayers():

                there_are_pours = utils.checkForPoursInLayer(pcb_layer)

                # Copper
                shapes = shapes_dict['copper'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['copper']['pads']['layer']
     
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', 
                                          transform=transform)

                    if component_type == 'components':
                        group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', component.getRefdef())
                    elif component_type == 'vias':
                        group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'via')
                        group.set('{'+config.cfg['ns']['pcbmode']+'}via', component.getFootprintName())
                    else:
                        pass

                    for shape in shapes:
                        place.placeShape(shape, group)
                        if there_are_pours == True:
                            mask_group = et.SubElement(self._masks[pcb_layer], 'g', 
                                                       transform=transform)
                            self._placeMask(mask_group, 
                                            shape,
                                            'pad')

                    # Add pin labels
                    labels = shapes_dict['pin-labels'][pcb_layer]
                    if labels != []:
                        style = utils.dictToStyleText(config.stl['layout']['board']['pad-labels'])
                        label_group = et.SubElement(group, 'g', 
                                                    transform="rotate(%s)" % component.getRotation(),
                                                    style=style)
                        for label in labels:
                            t = et.SubElement(label_group, 'text',
                                              x=str(label['location'][0]),
                                              # TODO: get rid of this hack
                                              y=str(-label['location'][1]))
                                              #y=str(-pin_location.y + pad_numbers_font_size/3),
                                              #refdef=self._refdef)
                            t.text = label['text']





                # Soldermask
                shapes = shapes_dict['soldermask'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['soldermask']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)
     
                    # Solderpaste
                    shapes = shapes_dict['solderpaste'][pcb_layer]
                    svg_layer = self._layers[pcb_layer]['solderpaste']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group, invert)


                # Silkscreen
                shapes = shapes_dict['silkscreen'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers[pcb_layer]['silkscreen']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    shape_group = et.SubElement(svg_layer, 'g', transform=transform)
                    shape_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
     
                    for shape in shapes:
                        # Refdefs need to be in their own groups so that their
                        # location can later be extracted, hence this...
                        try:
                            is_refdef = getattr(shape, 'is_refdef')
                        except:
                            is_refdef = False

                        if is_refdef == True:
                            refdef_group = et.SubElement(svg_layer, 'g', transform=transform)
                            refdef_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'refdef')
                            refdef_group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', refdef)
                            placed_element = place.placeShape(shape, refdef_group)
                        else:
                            placed_element = place.placeShape(shape, shape_group)


                # Assembly
                shapes = shapes_dict['assembly'][pcb_layer]
                if len(shapes) > 0: 
                    svg_layer = self._layers[pcb_layer]['assembly']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)

                # Drills
                shapes = shapes_dict['drills'][pcb_layer]
                if len(shapes) > 0:
                    svg_layer = self._layers['drills']['layer']
                    transform = "translate(%s,%s)" % (location[0],
                                                      config.cfg['invert-y']*location[1])
                    group = et.SubElement(svg_layer, 'g', transform=transform)
                    group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'component-shapes')
                    for shape in shapes:
                        placed_element = place.placeShape(shape, group)
                        placed_element.set('{'+config.cfg['ns']['pcbmode']+'}diameter',
                                           str(shape.getDiameter()))