Esempio n. 1
0
    def _placeDocs(self):
        """
        Places documentation blocks on the documentation layer
        """
        try:
            docs_dict = config.brd['documentation']
        except:
            return

        for key in docs_dict:

            location = utils.toPoint(docs_dict[key]['location'])
            docs_dict[key]['location'] = [0, 0]

            shape_group = et.SubElement(self._layers['documentation']['layer'],
                                        'g')
            shape_group.set('{' + config.cfg['ns']['pcbmode'] + '}type',
                            'module-shapes')
            shape_group.set('{' + config.cfg['ns']['pcbmode'] + '}doc-key',
                            key)
            shape_group.set(
                'transform', "translate(%s,%s)" %
                (location.x, config.cfg['invert-y'] * location.y))

            location = docs_dict[key]['location']
            docs_dict[key]['location'] = [0, 0]

            shape = Shape(docs_dict[key])
            style = Style(docs_dict[key], 'documentation')
            shape.setStyle(style)
            element = place.placeShape(shape, shape_group)
Esempio n. 2
0
    def _placeDocs(self):
        """
        Places documentation blocks on the documentation layer
        """
        try:
            docs_dict = config.brd['documentation']
        except:
            return

        for key in docs_dict:

            location = utils.toPoint(docs_dict[key]['location'])
            docs_dict[key]['location'] = [0, 0]

            shape_group = et.SubElement(self._layers['documentation']['layer'], 'g')
            shape_group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'module-shapes')            
            shape_group.set('{'+config.cfg['ns']['pcbmode']+'}doc-key', key)
            shape_group.set('transform', "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y))

            location = docs_dict[key]['location']
            docs_dict[key]['location'] = [0, 0]

            shape = Shape(docs_dict[key])
            style = Style(docs_dict[key], 'documentation')
            shape.setStyle(style)
            element = place.placeShape(shape, shape_group)
Esempio n. 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)
Esempio n. 4
0
def extractDocs(svg_in):
    """
    Extracts the position of the documentation elements and updates
    the board's json
    """

    # Get copper refdef shape groups from SVG data
    xpath_expr = '//svg:g[@pcbmode:sheet="documentation"]//svg:g[@pcbmode:type="module-shapes"]'
    docs = svg_in.findall(xpath_expr,
                          namespaces={
                              'pcbmode': config.cfg['ns']['pcbmode'],
                              'svg': config.cfg['ns']['svg']
                          })

    for doc in docs:
        doc_key = doc.get('{' + config.cfg['ns']['pcbmode'] + '}doc-key')
        translate_data = utils.parseTransform(doc.get('transform'))
        location = translate_data['location']
        location.y *= config.cfg['invert-y']

        current_location = utils.toPoint(
            config.brd['documentation'][doc_key]['location'])
        if current_location != location:
            config.brd['documentation'][doc_key]['location'] = [
                location.x, location.y
            ]
            msg.subInfo("Found new location ([%s, %s]) for '%s'" %
                        (location.x, location.y, doc_key))

    # Extract drill index location
    xpath_expr = '//svg:g[@pcbmode:sheet="drills"]//svg:g[@pcbmode:type="drill-index"]'
    drill_index = svg_in.find(xpath_expr,
                              namespaces={
                                  'pcbmode': config.cfg['ns']['pcbmode'],
                                  'svg': config.cfg['ns']['svg']
                              })
    transform_dict = utils.parseTransform(drill_index.get('transform'))
    location = transform_dict['location']
    location.y *= config.cfg['invert-y']

    # Modify the location in the board's config file. If a
    # 'drill-index' field doesn't exist, create it
    drill_index_dict = config.brd.get('drill-index')
    if drill_index_dict == None:
        config.brd['drill-index'] = {}
    config.brd['drill-index']['location'] = [location.x, location.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)
Esempio n. 5
0
def extractDocs(svg_in):
    """
    Extracts the position of the documentation elements and updates
    the board's json
    """

    # Get copper refdef shape groups from SVG data
    xpath_expr = '//svg:g[@pcbmode:sheet="documentation"]//svg:g[@pcbmode:type="module-shapes"]'
    docs = svg_in.findall(xpath_expr, 
                          namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                      'svg':config.cfg['ns']['svg']})

    
    for doc in docs:
        doc_key = doc.get('{'+config.cfg['ns']['pcbmode']+'}doc-key')
        translate_data = utils.parseTransform(doc.get('transform'))
        location = translate_data['location']
        location.y *= config.cfg['invert-y']

        current_location = utils.toPoint(config.brd['documentation'][doc_key]['location'])
        if current_location != location:
            config.brd['documentation'][doc_key]['location'] = [location.x, location.y] 
            msg.subInfo("Found new location ([%s, %s]) for '%s'" % (location.x, location.y, doc_key))


    # Extract drill index location
    xpath_expr = '//svg:g[@pcbmode:sheet="drills"]//svg:g[@pcbmode:type="drill-index"]'
    drill_index = svg_in.find(xpath_expr, 
                              namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                          'svg':config.cfg['ns']['svg']})    
    transform_dict = utils.parseTransform(drill_index.get('transform'))
    location = transform_dict['location']
    location.y *= config.cfg['invert-y']

    # Modify the location in the board's config file. If a
    # 'drill-index' field doesn't exist, create it
    drill_index_dict = config.brd.get('drill-index') 
    if drill_index_dict == None:
        config.brd['drill-index'] = {}
    config.brd['drill-index']['location'] = [location.x, location.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)
Esempio n. 6
0
    def _processPins(self):
        """
        Converts pins into 'shapes'
        """

        pins = self._footprint.get('pins') or {}

        for pin in pins:

            pin_location = pins[pin]['layout']['location'] or [0, 0]

            try:
                pad_name = pins[pin]['layout']['pad']
            except:
                msg.error("Each defined 'pin' must have a 'pad' name that is defined in the 'pads' dection of the footprint.")

            try:
                pad_dict = self._footprint['pads'][pad_name]
            except:
                msg.error("There doesn't seem to be a pad definition for pad '%s'." % pad_name)

            # Get the pin's rotation, if any
            pin_rotate = pins[pin]['layout'].get('rotate') or 0

            shapes = pad_dict.get('shapes') or []

            for shape_dict in shapes:

                shape_dict = shape_dict.copy()

                # Which layer(s) to place the shape on
                layers = utils.getExtendedLayerList(shape_dict.get('layers') or ['top'])

                # Add the pin's location to the pad's location
                shape_location = shape_dict.get('location') or [0, 0]
                shape_dict['location'] = [shape_location[0] + pin_location[0],
                                          shape_location[1] + pin_location[1]]

                # Add the pin's rotation to the pad's rotation
                shape_dict['rotate'] = (shape_dict.get('rotate') or 0) + pin_rotate

                # Determine if and which label to show
                show_name = pins[pin]['layout'].get('show-label') or True
                if show_name == True:
                    pin_label = pins[pin]['layout'].get('label') or pin

                for layer in layers:
                    
                    shape = Shape(shape_dict)
                    style = Style(shape_dict, 'conductor')
                    shape.setStyle(style)
                    try:
                        self._shapes['conductor'][layer].append(shape)
                    except:
                        self._shapes['conductor'][layer] = []
                        self._shapes['conductor'][layer].append(shape)
                        
                    for stype in ['soldermask','solderpaste']:

                        # Get a custom shape specification if it exists
                        sdict_list = shape_dict.get(stype) 

                        # Not defined; default
                        if sdict_list == None:
                            # Use default settings for shape based on
                            # the pad shape
                            sdict = shape_dict.copy()

                            # Which shape type is the pad?
                            shape_type = shape.getType()

                            # Apply modifier based on shape type
                            if shape_type == 'path':
                                sdict['scale'] = shape.getScale()*config.brd['distances'][stype]['path-scale']
                            elif shape_type in ['rect', 'rectangle']:
                                sdict['width'] += config.brd['distances'][stype]['rect-buffer']
                                sdict['height'] += config.brd['distances'][stype]['rect-buffer']
                            elif shape_type in ['circ', 'circle']:
                                sdict['diameter'] += config.brd['distances'][stype]['circle-buffer']
                            else:
                                pass

                            # Create shape based on new dictionary
                            sshape = Shape(sdict)

                            # Define style
                            sstyle = Style(sdict, stype)

                            # Apply style
                            sshape.setStyle(sstyle)

                            # Add shape to footprint's shape dictionary
                            #self._shapes[stype][layer].append(sshape)
                            try:
                                self._shapes[stype][layer].append(sshape)
                            except:
                                self._shapes[stype][layer] = []
                                self._shapes[stype][layer].append(sshape)



                        # Do not place shape
                        elif (sdict_list == {}) or (sdict_list == []):
                            pass

                        # Custom shape definition
                        else:

                            # If dict (as before support of multiple
                            # shapes) then append to a single element
                            # list
                            if type(sdict_list) is dict:
                                sdict_list = [sdict_list]

                            # Process list of shapes
                            for sdict_ in sdict_list:
                                sdict = sdict_.copy()
                                shape_loc = utils.toPoint(sdict.get('location') or [0, 0])

                                # Apply rotation
                                sdict['rotate'] = (sdict.get('rotate') or 0) + pin_rotate

                                # Rotate location
                                shape_loc.rotate(pin_rotate, Point())

                                sdict['location'] = [shape_loc.x + pin_location[0],
                                                     shape_loc.y + pin_location[1]]

                                # Create new shape
                                sshape = Shape(sdict)
     
                                # Create new style
                                sstyle = Style(sdict, stype)
                                
                                # Apply style
                                sshape.setStyle(sstyle)
     
                                # Add shape to footprint's shape dictionary
                                #self._shapes[stype][layer].append(sshape)
                                try:
                                    self._shapes[stype][layer].append(sshape)
                                except:
                                    self._shapes[stype][layer] = []
                                    self._shapes[stype][layer].append(sshape)

     
                    # Add pin label
                    if (pin_label != None):
                        shape.setLabel(pin_label)




            drills = pad_dict.get('drills') or []
            for drill_dict in drills:
                drill_dict = drill_dict.copy()
                drill_dict['type'] = drill_dict.get('type') or 'drill'
                drill_location = drill_dict.get('location') or [0, 0]
                drill_dict['location'] = [drill_location[0] + pin_location[0],
                                          drill_location[1] + pin_location[1]]
                shape = Shape(drill_dict)
                style = Style(drill_dict, 'drills')
                shape.setStyle(style)
                try:
                    self._shapes['drills']['top'].append(shape)
                except:
                    self._shapes['drills']['top'] = []
                    self._shapes['drills']['top'].append(shape)
Esempio n. 7
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
Esempio n. 8
0
    def _placeDrillIndex(self):
        """
        Adds a drill index
        """

        # Get the drills sheet / SVG layer
        drill_layer = self._layers['drills']['layer']
        ns = {
            'pcbmode': config.cfg['ns']['pcbmode'],
            'svg': config.cfg['ns']['svg']
        }
        drills = drill_layer.findall(".//*[@pcbmode:diameter]", namespaces=ns)

        drills_dict = {}
        largest_drill = 0
        drill_count = 0
        for drill in drills:
            diameter = drill.get('{' + config.cfg['ns']['pcbmode'] +
                                 '}diameter')
            diameter = round(float(diameter), 2)
            if diameter not in drills_dict:
                drills_dict[diameter] = 1
            else:
                drills_dict[diameter] += 1
            if diameter > largest_drill:
                largest_drill = diameter
            drill_count += 1

        # Get location, or generate one
        try:
            location = config.brd['drill-index']['location']
        except:
            # If not location is specified, put the drill index at the
            # bottom left 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, -(self._height / 2 + gap)]
        location = utils.toPoint(location)

        # Create group for placing index
        transform = "translate(%s,%s)" % (location.x,
                                          config.cfg['invert-y'] * location.y)
        group = et.SubElement(drill_layer, 'g', transform=transform)
        group.set('{' + config.cfg['ns']['pcbmode'] + '}type', 'drill-index')

        text_style_dict = config.stl['layout']['drill-index'].get('text')
        text_style = utils.dict_to_style(text_style_dict)

        count_style_dict = config.stl['layout']['drill-index'].get(
            'count-text')
        count_style = utils.dict_to_style(count_style_dict)

        count_style_dict['font-size'] /= 2
        drill_size_style = utils.dict_to_style(count_style_dict)

        if drill_count == 0:
            text = 'No drills'
        elif drill_count == 1:
            text = '1 drill: '
        else:
            text = '%s drills: ' % drill_count
        t = et.SubElement(group, 'text', x=str(0), y=str(0), style=text_style)
        t.text = text

        # "new line"
        location.y = -(largest_drill / 2 + 0.5)
        location.x = largest_drill / 2

        gap = 2

        for diameter in reversed(sorted(drills_dict)):
            path = svg.drillPath(diameter)
            transform = "translate(%s,%s)" % (
                location.x, config.cfg['invert-y'] * location.y)
            element = et.SubElement(group, 'path', d=path, transform=transform)
            element.set("fill-rule", "evenodd")

            t = et.SubElement(group,
                              'text',
                              x=str(location.x),
                              y=str(-location.y),
                              dy="%s" % (config.cfg['invert-y'] * 0.25),
                              style=count_style)
            t.text = str(drills_dict[diameter])

            t = et.SubElement(group,
                              'text',
                              x=str(location.x),
                              y=str(-location.y),
                              dy="%s" % (config.cfg['invert-y'] * -0.5),
                              style=drill_size_style)
            t.text = "%s mm" % diameter

            location.x += max(diameter, 2.5)
Esempio n. 9
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)
Esempio n. 10
0
def extractComponents(svg_in):
    """
    """

    xpath_expr_place = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="placement"]//svg:g[@pcbmode:type="%s"]'

    for pcb_layer in config.stk['surface-layer-names']:

        # Find all 'component' markers
        markers = svg_in.findall(xpath_expr_place % (pcb_layer, 'component'),
                                 namespaces={
                                     'pcbmode': config.cfg['ns']['pcbmode'],
                                     'svg': config.cfg['ns']['svg']
                                 })
        # Find all 'shape' markers
        markers += svg_in.findall(xpath_expr_place % (pcb_layer, 'shape'),
                                  namespaces={
                                      'pcbmode': config.cfg['ns']['pcbmode'],
                                      'svg': config.cfg['ns']['svg']
                                  })

        for marker in markers:

            transform_data = utils.parseTransform(marker.get('transform'))
            refdef = marker.get('{' + config.cfg['ns']['pcbmode'] + '}refdef')
            marker_type = marker.get('{' + config.cfg['ns']['pcbmode'] +
                                     '}type')

            if marker_type == 'component':
                comp_dict = config.brd['components'][refdef]
            elif marker_type == 'shape':
                comp_dict = config.brd['shapes'][refdef]
            else:
                continue

            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:
                x1 = utils.niceFloat(old_location.x)
                y1 = utils.niceFloat(old_location.y)
                x2 = utils.niceFloat(new_location.x)
                y2 = utils.niceFloat(new_location.y)
                msg.subInfo("%s has moved from [%s,%s] to [%s,%s]" %
                            (refdef, x1, y2, x2, y2))
                # Apply new location
                comp_dict['location'] = [x2, y2]

            # 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'] = utils.niceFloat(
                    (old_rotate + new_rotate) % 360)
                msg.subInfo("Component %s rotated from %s to %s" %
                            (refdef, old_rotate, comp_dict['rotate']))

    # 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
Esempio n. 11
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
Esempio n. 12
0
def extractRefdefs(svg_in):
    """
    """

    xpath_refdefs = '//svg:g[@pcbmode:sheet="silkscreen"]//svg:g[@pcbmode:type="refdef"]'
    refdefs_elements = svg_in.findall(xpath_refdefs,
                                      namespaces={
                                          'pcbmode':
                                          config.cfg['ns']['pcbmode'],
                                          'svg': config.cfg['ns']['svg']
                                      })

    for refdef_element in refdefs_elements:

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

        # Get reference designator
        refdef = refdef_element.get('{' + config.cfg['ns']['pcbmode'] +
                                    '}refdef')

        # Get component dictionary
        refdef_dict = config.brd['components'].get(refdef)

        # Get component placement layer
        comp_loc = utils.toPoint(refdef_dict.get('location', [0, 0]))

        if comp_loc != group_loc:

            # Get location of the refdef from the component dict
            try:
                loc_old = utils.toPoint(
                    refdef_dict['silkscreen']['refdef']['location'])
            except:
                loc_old = Point()

            # Get component placement layer
            comp_layer = refdef_dict.get('layer', 'top')

            # Get component rotation
            comp_rotation = refdef_dict.get('rotate', 0)

            difference = group_loc - comp_loc
            difference.rotate(-comp_rotation, Point())

            if comp_layer == 'bottom':
                difference.x *= -1

            loc_new = loc_old + difference

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

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

            x = utils.niceFloat(loc_new.x)
            y = utils.niceFloat(loc_new.y)
            refdef_dict['silkscreen']['refdef']['location'] = [x, 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
Esempio n. 13
0
    def __init__(self, shape):

        gerber_lp = None
        mirror = False

        self._shape_dict = shape

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            self._rotate += 180

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

        self._path = SvgPath(path, gerber_lp)

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

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

        self._location = utils.toPoint(shape.get('location', [0, 0]))
Esempio n. 14
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
Esempio n. 15
0
def extractComponents(svg_in):
    """
    """
    
    xpath_expr_place = '//svg:g[@pcbmode:pcb-layer="%s"]//svg:g[@pcbmode:sheet="placement"]//svg:g[@pcbmode:type="%s"]'

    for pcb_layer in config.stk['surface-layer-names']:
        
        # Find all 'component' markers
        markers = svg_in.findall(xpath_expr_place % (pcb_layer, 'component'), 
                                 namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                             'svg':config.cfg['ns']['svg']})
        # Find all 'shape' markers
        markers += svg_in.findall(xpath_expr_place % (pcb_layer, 'shape'), 
                                  namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                              'svg':config.cfg['ns']['svg']})

        for marker in markers:

            transform_data = utils.parseTransform(marker.get('transform'))
            refdef = marker.get('{'+config.cfg['ns']['pcbmode']+'}refdef')
            marker_type = marker.get('{'+config.cfg['ns']['pcbmode']+'}type')
            
            if marker_type == 'component':
                comp_dict = config.brd['components'][refdef]
            elif marker_type == 'shape':
                comp_dict = config.brd['shapes'][refdef]
            else:
                continue

            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:
                x1 = utils.niceFloat(old_location.x)
                y1 = utils.niceFloat(old_location.y)
                x2 = utils.niceFloat(new_location.x)
                y2 = utils.niceFloat(new_location.y)
                msg.subInfo("%s has moved from [%s,%s] to [%s,%s]" % (refdef,x1,y2,x2,y2))
                # Apply new location
                comp_dict['location'] = [x2,y2]

            # 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'] = utils.niceFloat((old_rotate+new_rotate) % 360)
                msg.subInfo("Component %s rotated from %s to %s" % (refdef, 
                                                                    old_rotate, 
                                                                    comp_dict['rotate']))


    # 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
Esempio n. 16
0
def extractRefdefs(svg_in):
    """
    """

    xpath_refdefs = '//svg:g[@pcbmode:sheet="silkscreen"]//svg:g[@pcbmode:type="refdef"]'
    refdefs_elements = svg_in.findall(xpath_refdefs, 
                             namespaces={'pcbmode':config.cfg['ns']['pcbmode'],
                                         'svg':config.cfg['ns']['svg']})

    for refdef_element in refdefs_elements:
 
        # Get refdef group location
        group_trans_data = utils.parseTransform(refdef_element.get('transform'))
        group_loc = group_trans_data['location']
        # Invert 'y' coordinate because Inkscape
        group_loc.y *= config.cfg['invert-y']

        # Get reference designator
        refdef = refdef_element.get('{'+config.cfg['ns']['pcbmode']+'}refdef')

        # Get component dictionary
        refdef_dict = config.brd['components'].get(refdef)

        # Get component placement layer
        comp_loc = utils.toPoint(refdef_dict.get('location', [0,0]))

        if comp_loc != group_loc:

            # Get location of the refdef from the component dict
            try:
                loc_old = utils.toPoint(refdef_dict['silkscreen']['refdef']['location'])
            except:
                loc_old = Point()

            # Get component placement layer
            comp_layer = refdef_dict.get('layer', 'top')
     
            # Get component rotation
            comp_rotation = refdef_dict.get('rotate', 0)

            difference = group_loc-comp_loc
            difference.rotate(-comp_rotation, Point())

            if comp_layer == 'bottom':
                difference.x *= -1

            loc_new = loc_old+difference

            try:
                tmp = refdef_dict['silkscreen']
            except:
                comp_dict['silkscreen'] = {}
 
            try:
                tmp = refdef_dict['silkscreen']['refdef']
            except:
                refdef_dict['silkscreen']['refdef'] = {}
 
            x = utils.niceFloat(loc_new.x)
            y = utils.niceFloat(loc_new.y)
            refdef_dict['silkscreen']['refdef']['location'] = [x,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
Esempio n. 17
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
Esempio n. 18
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
Esempio n. 19
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
Esempio n. 20
0
    def __init__(self, shape):

        gerber_lp = None
        mirror = False

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

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

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

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

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

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

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

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

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

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

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

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

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

            self._rotate += 180

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


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

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

        self._location = utils.toPoint(shape.get('location', [0, 0]))
Esempio n. 21
0
    def _placeDrillIndex(self):
        """
        Adds a drill index
        """

        # Get the drills sheet / SVG layer
        drill_layer = self._layers['drills']['layer']
        ns = {'pcbmode':config.cfg['ns']['pcbmode'],
              'svg':config.cfg['ns']['svg']}
        drills = drill_layer.findall(".//*[@pcbmode:diameter]", namespaces=ns)

        drills_dict = {}
        longest_text = 0
        largest_drill = 0
        drill_count = 0
        for drill in drills:
            diameter = drill.get('{'+config.cfg['ns']['pcbmode']+'}diameter')
            diameter = round(float(diameter), 2)
            if diameter not in drills_dict:
                drills_dict[diameter] = 1
            else:
                drills_dict[diameter] += 1
            if diameter > largest_drill:
                largest_drill = diameter
            drill_count += 1

            if len(str(diameter)) > longest_text:
                longest_text = len(str(diameter))


        # Get location, or generate one
        try:
            location = config.brd['drill-index']['location']
        except:
            # If not location is specified, put the drill index at the
            # bottom left 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, -(self._height/2+gap)]
        location = utils.toPoint(location)        

        # Create group for placing index
        transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y)
        group = et.SubElement(drill_layer, 'g',
                              transform=transform)
        group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'drill-index')
        

        text_style_dict = config.stl['layout']['drill-index'].get('text')
        text_style = utils.dictToStyleText(text_style_dict)
 
        count_style_dict = config.stl['layout']['drill-index'].get('count-text')
        count_style = utils.dictToStyleText(count_style_dict)        

        count_style_dict['font-size'] /= 2
        drill_size_style = utils.dictToStyleText(count_style_dict)

        if drill_count == 0:
            text = 'No drills'
        elif drill_count == 1:
            text = '1 drill: '
        else:
            text = '%s drills: ' % drill_count
        t = et.SubElement(group, 'text',
                          x=str(0),
                          y=str(0),
                          style=text_style)
        t.text = text

        # "new line"
        location.y = -(largest_drill/2 + 1.5)

        # TODO: this hack'ish thing for aligning the text isn't going
        # to work when the font is changed in the stylesheet
        if float(longest_text*0.5) > largest_drill:
            location.x = longest_text*0.3
        else:
            location.x = largest_drill/2

        gap = 2

        for diameter in reversed(sorted(drills_dict)):
            path = svg.drillPath(diameter)
            transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y)
            element = et.SubElement(group, 'path',
                                    d=path,
                                    transform=transform)
            element.set("fill-rule", "evenodd")

            t = et.SubElement(group, 'text',
                              x=str(location.x),
                              y=str(-location.y),
                              dy="%s" % (config.cfg['invert-y']*0.25),
                              style=count_style)
            t.text = str(drills_dict[diameter])

            t = et.SubElement(group, 'text',
                              x=str(location.x),
                              y=str(-location.y),
                              dy="%s" % (config.cfg['invert-y']*-0.5),
                              style=drill_size_style)
            t.text = "%s mm" % diameter

            location.x += max(diameter, 2.5)