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)
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 config.stk['layer-names']: 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)
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)
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)
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)
def _placeMask(self, svg_layer, shape, kind, original=False, mirror=False): """ Places a mask of a shape of type 'Shape' on SVG layer 'svg_layer'. 'kind' : type of shape; used to fetch the correct distance to pour 'original': use the original path, not the transformed one """ # Get the desired distance based on 'kind' 'outline', 'drill', # 'pad', 'route' unless 'pour_buffer' is specified pour_buffer = shape.getPourBuffer() if pour_buffer == None: try: pour_buffer = self._module_dict['distances']['from-pour-to'][kind] except: pour_buffer = config.brd['distances']['from-pour-to'][kind] style_template = "fill:%s;stroke:#000;stroke-linejoin:round;stroke-width:%s;stroke-linecap:round;" style = shape.getStyle() if (pour_buffer > 0): mask_element = place.placeShape(shape, svg_layer, mirror, original) if style.getStyleType() == 'fill': mask_element.set('style', style_template % ('#000', pour_buffer*2)) else: # This width provides a distance of 'pour_buffer' from the # edge of the trace to a pour width = style.getStrokeWidth() + pour_buffer*2 mask_element.set('style', style_template % ('none', width)) path = shape.getOriginalPath().lower() segments = path.count('m') mask_element.set('{'+config.cfg['ns']['pcbmode']+'}gerber-lp', 'c'*segments)
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])
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])
def _placeMask(self, svg_layer, shape, kind, original=False, mirror=False): """ Places a mask of a shape of type 'Shape' on SVG layer 'svg_layer'. 'kind' : type of shape; used to fetch the correct distance to pour 'original': use the original path, not the transformed one """ # Get the desired distance based on 'kind' 'outline', 'drill', # 'pad', 'route' unless 'pour_buffer' is specified pour_buffer = shape.getPourBuffer() if pour_buffer == None: try: pour_buffer = self._module_dict['distances']['from-pour-to'][ kind] except: pour_buffer = config.brd['distances']['from-pour-to'][kind] style_template = "fill:%s;stroke:#000;stroke-linejoin:round;stroke-width:%s;stroke-linecap:round;" style = shape.getStyle() if (pour_buffer > 0): mask_element = place.placeShape(shape, svg_layer, mirror, original) if style.getStyleType() == 'fill': mask_element.set('style', style_template % ('#000', pour_buffer * 2)) else: # This width provides a distance of 'pour_buffer' from the # edge of the trace to a pour width = style.getStrokeWidth() + pour_buffer * 2 mask_element.set('style', style_template % ('none', width)) path = shape.getOriginalPath().lower() segments = path.count('m') mask_element.set('{' + config.cfg['ns']['pcbmode'] + '}gerber-lp', 'c' * segments)
def _placeComponents(self, components, component_type, print_refdef=False): """ Places the component on the board. 'component_type' is the content of the 'type' fiels of the placed group. This is used by the extractor to identify the type of component ('component', 'via', 'shape') """ htmlpar = HTMLParser.HTMLParser() for component in components: shapes_dict = component.getShapes() location = component.getLocation() rotation = component.getRotation() 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 config.stk['layer-names']: there_are_pours = utils.checkForPoursInLayer(pcb_layer) # Copper shapes = shapes_dict['conductor'].get(pcb_layer) or [] if len(shapes) > 0: svg_layer = self._layers[pcb_layer]['conductor']['pads'][ '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_type) # Add the reference designator as well if it's a # 'component' if component_type == 'component': shape_group.set( '{' + config.cfg['ns']['pcbmode'] + '}refdef', component.getRefdef()) style = utils.dictToStyleText( config.stl['layout']['conductor']['pads']['labels']) label_group = et.SubElement(shape_group, 'g', style=style) for shape in shapes: place.placeShape(shape, shape_group, invert) # Add pin labels # TODO: This isn't perfect, but good enough for now label = shape.getLabel() if label != None: label_location = shape.getLocation() label_rotation = shape.getRotation() label_transform = "rotate(%s)" % label_rotation t = et.SubElement(label_group, 'text', x=str(((1, -1)[invert]) * label_location.x), y=str(config.cfg['invert-y'] * label_location.y), transform=label_transform) t.text = label 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) # Pours shapes = shapes_dict['pours'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['conductor']['pours'][ 'layer'] shape_group = et.SubElement(svg_layer, 'g', mask='url(#mask-%s)' % pcb_layer) except: svg_layer = None if len(shapes) > 0 and svg_layer != None: transform = "translate(%s,%s)" % ( location[0], config.cfg['invert-y'] * location[1]) group = et.SubElement(shape_group, 'g', transform=transform) group.set('{' + config.cfg['ns']['pcbmode'] + '}type', 'pours') for shape in shapes: placed_element = place.placeShape(shape, group, invert) # Soldermask shapes = shapes_dict['soldermask'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['soldermask']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['solderpaste']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['silkscreen']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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: # Shapes don't need to have silkscreen # reference designators if component_type != 'shape': 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['assembly']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] 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())) # Place component origin marker svg_layer = self._layers[placement_layer]['placement']['layer'] # Here pcb_layer may not exist for components that define # shapes for internal layers but only surface layers are # defined in the stackup try: group = et.SubElement(svg_layer, 'g', transform=transform) except: return group.set('{' + config.cfg['ns']['pcbmode'] + '}type', component_type) group.set('{' + config.cfg['ns']['pcbmode'] + '}footprint', component.getFootprintName()) if (component_type == 'component') or (component_type == 'shape'): group.set('{' + config.cfg['ns']['pcbmode'] + '}refdef', refdef) path = svg.placementMarkerPath() transform = "translate(%s,%s)" % ( location[0], config.cfg['invert-y'] * location[1]) if placement_layer == 'bottom': rotation *= -1 marker_element = et.SubElement(group, 'path', d=path, transform="rotate(%s)" % rotation) if (component_type == 'component'): style = utils.dictToStyleText( config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.17", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "%s" % (refdef) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) elif (component_type == 'shape'): style = utils.dictToStyleText( config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.17", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "%s" % (refdef) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) elif (component_type == 'via'): style = utils.dictToStyleText( config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.11", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) else: continue
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)
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)
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()))
def _placeOutlineDimensions(self): """ Places outline dimension arrows """ def makeArrow(width, gap): """ Returns a path for an arrow of width 'width' with a center gap of width 'gap' """ # Length of bar perpendicular to the arrow's shaft base_length = 1.8 # Height of arrow's head arrow_height = 2.5 # Width of arrow's head arrow_base = 1.2 # Create path path = "m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s" % ( -gap / 2, 0, -width / 2 + gap / 2, 0, 0, base_length / 2, 0, -base_length, arrow_height, (base_length - arrow_base) / 2, -arrow_height, arrow_base / 2, arrow_height, arrow_base / 2, -arrow_height, -arrow_base / 2, width / 2, 0, gap / 2, 0, width / 2 - gap / 2, 0, 0, base_length / 2, 0, -base_length, -arrow_height, (base_length - arrow_base) / 2, arrow_height, arrow_base / 2, -arrow_height, arrow_base / 2, arrow_height, -arrow_base / 2, ) return path # Create text shapes shape_dict = {} shape_dict['type'] = 'text' style_dict = config.stl['layout']['dimensions'].get('text') or {} shape_dict['font-family'] = style_dict.get( 'font-family') or "UbuntuMono-R-webfont" shape_dict['font-size'] = style_dict.get('font-size') or "1.5mm" shape_dict['line-height'] = style_dict.get('line-height') or "1mm" shape_dict['letter-spacing'] = style_dict.get( 'letter-spacing') or "0mm" # Locations arrow_gap = 1.5 width_loc = [0, self._height / 2 + arrow_gap] height_loc = [-(self._width / 2 + arrow_gap), 0] # Width text width_text_dict = shape_dict.copy() width_text_dict['value'] = "%s mm" % round(self._width, 2) width_text_dict['location'] = width_loc width_text = Shape(width_text_dict) style = Style(width_text_dict, 'dimensions') width_text.setStyle(style) # Height text height_text_dict = shape_dict.copy() height_text_dict['value'] = "%s mm" % round(self._height, 2) height_text_dict['rotate'] = -90 height_text_dict['location'] = height_loc height_text = Shape(height_text_dict) style = Style(height_text_dict, 'dimensions') height_text.setStyle(style) # Width arrow shape_dict = {} shape_dict['type'] = 'path' shape_dict['value'] = makeArrow(self._width, width_text.getWidth() * 1.2) shape_dict['location'] = width_loc width_arrow = Shape(shape_dict) style = Style(shape_dict, 'dimensions') width_arrow.setStyle(style) # Height arrow shape_dict = {} shape_dict['type'] = 'path' shape_dict['value'] = makeArrow(self._height, height_text.getHeight() * 1.2) shape_dict['rotate'] = -90 shape_dict['location'] = height_loc height_arrow = Shape(shape_dict) style = Style(shape_dict, 'dimensions') height_arrow.setStyle(style) svg_layer = self._layers['dimensions']['layer'] group = et.SubElement(svg_layer, 'g') group.set('{' + config.cfg['ns']['pcbmode'] + '}type', 'module-shapes') place.placeShape(width_text, group) place.placeShape(height_text, group) place.placeShape(width_arrow, group) place.placeShape(height_arrow, group)
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()))
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()))
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)
def _placeOutlineDimensions(self): """ Places outline dimension arrows """ def makeArrow(width, gap): """ Returns a path for an arrow of width 'width' with a center gap of width 'gap' """ # Length of bar perpendicular to the arrow's shaft base_length = 1.8 # Height of arrow's head arrow_height = 2.5 # Width of arrow's head arrow_base = 1.2 # Create path path = "m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s m %s,%s %s,%s" % (-gap/2,0, -width/2+gap/2,0, 0,base_length/2, 0,-base_length, arrow_height,(base_length-arrow_base)/2, -arrow_height,arrow_base/2, arrow_height,arrow_base/2, -arrow_height,-arrow_base/2, width/2,0, gap/2,0, width/2-gap/2,0, 0,base_length/2, 0,-base_length, -arrow_height,(base_length-arrow_base)/2, arrow_height,arrow_base/2, -arrow_height,arrow_base/2, arrow_height,-arrow_base/2,) return path # Create text shapes shape_dict = {} shape_dict['type'] = 'text' style_dict = config.stl['layout']['dimensions'].get('text') or {} shape_dict['font-family'] = style_dict.get('font-family') or "UbuntuMono-R-webfont" shape_dict['font-size'] = style_dict.get('font-size') or "1.5mm" shape_dict['line-height'] = style_dict.get('line-height') or "1mm" shape_dict['letter-spacing'] = style_dict.get('letter-spacing') or "0mm" # Locations arrow_gap = 1.5 width_loc = [0, self._height/2+arrow_gap] height_loc = [-(self._width/2+arrow_gap), 0] # Width text width_text_dict = shape_dict.copy() width_text_dict['value'] = "%s mm" % round(self._width,2) width_text_dict['location'] = width_loc width_text = Shape(width_text_dict) style = Style(width_text_dict, 'dimensions') width_text.setStyle(style) # Height text height_text_dict = shape_dict.copy() height_text_dict['value'] = "%s mm" % round(self._height,2) height_text_dict['rotate'] = -90 height_text_dict['location'] = height_loc height_text = Shape(height_text_dict) style = Style(height_text_dict, 'dimensions') height_text.setStyle(style) # Width arrow shape_dict = {} shape_dict['type'] = 'path' shape_dict['value'] = makeArrow(self._width, width_text.getWidth()*1.2) shape_dict['location'] = width_loc width_arrow = Shape(shape_dict) style = Style(shape_dict, 'dimensions') width_arrow.setStyle(style) # Height arrow shape_dict = {} shape_dict['type'] = 'path' shape_dict['value'] = makeArrow(self._height, height_text.getHeight()*1.2) shape_dict['rotate'] = -90 shape_dict['location'] = height_loc height_arrow = Shape(shape_dict) style = Style(shape_dict, 'dimensions') height_arrow.setStyle(style) svg_layer = self._layers['dimensions']['layer'] group = et.SubElement(svg_layer, 'g') group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'module-shapes') place.placeShape(width_text, group) place.placeShape(height_text, group) place.placeShape(width_arrow, group) place.placeShape(height_arrow, group)
def _placeRouting(self): """ """ routing = config.rte routes = routing.get('routes') or {} # 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 config.stk['layer-names']: # 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]['conductor']['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, 'conductor') 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)
def _placeComponents(self, components, component_type, print_refdef=False): """ Places the component on the board. 'component_type' is the content of the 'type' fiels of the placed group. This is used by the extractor to identify the type of component ('component', 'via', 'shape') """ htmlpar = HTMLParser.HTMLParser() for component in components: shapes_dict = component.getShapes() location = component.getLocation() rotation = component.getRotation() 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 config.stk['layer-names']: there_are_pours = utils.checkForPoursInLayer(pcb_layer) # Copper shapes = shapes_dict['conductor'].get(pcb_layer) or [] if len(shapes) > 0: svg_layer = self._layers[pcb_layer]['conductor']['pads']['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_type) # Add the reference designator as well if it's a # 'component' if component_type == 'component': shape_group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', component.getRefdef()) style = utils.dictToStyleText(config.stl['layout']['conductor']['pads']['labels']) label_group = et.SubElement(shape_group, 'g', style=style) for shape in shapes: place.placeShape(shape, shape_group, invert) # Add pin labels # TODO: This isn't perfect, but good enough for now label = shape.getLabel() if label != None: label_location = shape.getLocation() label_rotation = shape.getRotation() label_transform = "rotate(%s)" % label_rotation t = et.SubElement(label_group, 'text', x=str(((1,-1)[invert])*label_location.x), y=str(config.cfg['invert-y']*label_location.y), transform=label_transform) t.text = label 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) # Pours shapes = shapes_dict['pours'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['conductor']['pours']['layer'] shape_group = et.SubElement(svg_layer, 'g', mask='url(#mask-%s)' % pcb_layer) except: svg_layer = None if len(shapes) > 0 and svg_layer != None: transform = "translate(%s,%s)" % (location[0], config.cfg['invert-y']*location[1]) group = et.SubElement(shape_group, 'g', transform=transform) group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'pours') for shape in shapes: placed_element = place.placeShape(shape, group, invert) # Soldermask shapes = shapes_dict['soldermask'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['soldermask']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['solderpaste']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['silkscreen']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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: # Shapes don't need to have silkscreen # reference designators if component_type != 'shape': 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'].get(pcb_layer) or [] try: svg_layer = self._layers[pcb_layer]['assembly']['layer'] except: svg_layer = None if len(shapes) > 0 and svg_layer != None: 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'].get(pcb_layer) or [] 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())) # Place component origin marker svg_layer = self._layers[placement_layer]['placement']['layer'] # Here pcb_layer may not exist for components that define # shapes for internal layers but only surface layers are # defined in the stackup try: group = et.SubElement(svg_layer, 'g', transform=transform) except: return group.set('{'+config.cfg['ns']['pcbmode']+'}type', component_type) group.set('{'+config.cfg['ns']['pcbmode']+'}footprint', component.getFootprintName()) if (component_type == 'component') or (component_type == 'shape'): group.set('{'+config.cfg['ns']['pcbmode']+'}refdef', refdef) path = svg.placementMarkerPath() transform = "translate(%s,%s)" % (location[0], config.cfg['invert-y']*location[1]) if placement_layer == 'bottom': rotation *= -1 marker_element = et.SubElement(group, 'path', d=path, transform="rotate(%s)" % rotation) if (component_type == 'component'): style = utils.dictToStyleText(config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.17", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "%s" % (refdef) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) elif (component_type == 'shape'): style = utils.dictToStyleText(config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.17", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "%s" % (refdef) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) elif (component_type == 'via'): style = utils.dictToStyleText(config.stl['layout']['placement']['text']) t = et.SubElement(group, 'text', x="0", y="-0.11", style=style) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = htmlpar.unescape("%s°" % (rotation)) ts = et.SubElement(t, 'tspan', x="0", dy="0.1") ts.text = "[%.2f,%.2f]" % (location[0], location[1]) else: continue
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)
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()))