import operator
from math import cos, sin, atan2, radians, degrees, sqrt

import koko.lib.shapes2d as s2d
from koko.lib.text import text
from numpy import *

class PCB(object):
    def __init__(self, x0, y0, width, height, chamfer_distance=0):
        self.x0 = x0
        self.y0 = y0
        self.width = width
        self.height = height

        self.components  = []
        self.connections = []
        self._cutout = None
        self.custom_cutout = None
        self.custom_layers = {}
        self.chamfer_distance = chamfer_distance

    @property
    def traces(self):
        L = [c.pads for c in self.components if c.side == 0] + [c.traces[0] for c in self.connections]
        if L:
            t = reduce(operator.add, L)
            #L = [c.holes for c in self.components if c.holes is not None]
            #L.extend([c.holes for c in self.connections if c.holes is not None])
            #if L:
            #    t = t - reduce(operator.add,L)
            return t
        else: return None
    @property
    def traces_other_side(self):
        L = [c.pads for c in self.components if c.side == 1] + [c.traces[1] for c in self.connections if c.traces[1] is not None]
        if L:
            t = reduce(operator.add, L)
            #L = [c.holes for c in self.components if c.holes is not None]
            #L.extend([c.holes for c in self.connections if c.holes is not None])
            #if L:
            #    t = t - reduce(operator.add,L)
            return t
        else: return None        
    @property
    def holes(self):
        L = [c.holes for c in self.components if c.holes is not None]
        L.extend([c.holes for c in self.connections if c.holes is not None])
        if L:
            t = reduce(operator.add,L)  
            return t
        else:
            return None

    @property
    def part_labels_top(self):
        L = [c.label for c in self.components if c.label is not None and c.side==0]
        return reduce(operator.add, L) if L else None
    @property
    def part_labels_bot(self):
        L = [c.label for c in self.components if c.label is not None and c.side==1]
        return reduce(operator.add, L) if L else None
    @property
    def part_shadows_top(self):
        L = [c.shadow_shape for c in self.components if c.shadow_shape is not None and c.side==0]
        return reduce(operator.add, L) if L else None 
    @property
    def part_shadows_bot(self):
        L = [c.shadow_shape for c in self.components if c.shadow_shape is not None and c.side==1]
        return reduce(operator.add, L) if L else None        
    @property
    def pin_labels_top(self):
        L = [c.pin_labels for c in self.components if c.pin_labels is not None and c.side==0]
        return reduce(operator.add, L) if L else None
    @property
    def pin_labels_bot(self):
        L = [c.pin_labels for c in self.components if c.pin_labels is not None and c.side==1]
        return reduce(operator.add, L) if L else None

    @property
    def cutout(self):
        if self.custom_cutout is not None:    
            if self.holes:
                return self.custom_cutout - self.holes
            else: 
                return self.custom_cutout 
        outer = s2d.rectangle(self.x0, self.x0 + self.width,
                             self.y0, self.y0 + self.height)
        if self.chamfer_distance:
            c = self.chamfer_distance
            c1 = s2d.triangle(self.x0,self.y0,self.x0,self.y0+c,self.x0+c,self.y0)
            c2 = s2d.triangle(self.x0+self.width,self.y0+self.height, self.x0+self.width, self.y0+self.height-c, self.x0+self.width-c, self.y0+self.height)
            c3 = s2d.triangle(self.x0,self.y0+self.height, self.x0+c, self.y0+self.height, self.x0, self.y0+self.height-c)
            c4 = s2d.triangle(self.x0+self.width,self.y0, self.x0+self.width-c, self.y0, self.x0+self.width, self.y0+c)
            outer -= c1+c2+c3+c4
        #L = [c.holes for c in self.components if c.holes is not None]
        #L.extend([c.holes for c in self.connections if c.holes is not None])
        return outer - self.holes if self.holes else outer

    #@property
    def layout(self,sides=[0,1]):
        T = []
        if 0 in sides:
            if self.part_labels_top:
                T.append(s2d.color(self.part_labels_top, (125, 200, 60)))
            if self.pin_labels_top:
                T.append(s2d.color(self.pin_labels_top, (255, 90, 60)))
            if self.traces:
                T.append(s2d.color(self.traces-self.holes, (125, 90, 60)))
            if self.part_shadows_top:
                T.append(s2d.color(self.part_shadows_top-self.holes,(55,55,60)))
        if 1 in sides:
            if self.part_labels_bot:
                T.append(s2d.color(self.part_labels_bot, (90, 60, 255)))
            if self.pin_labels_bot:
                T.append(s2d.color(self.pin_labels_bot, (175, 30, 175)))
            if self.traces_other_side:
                T.append(s2d.color(self.traces_other_side-self.holes, (90, 60, 125)))
            if self.part_shadows_bot:
               T.append(s2d.color(self.part_shadows_bot-self.holes,(45, 30, 62)))
        
        for v in sorted(self.custom_layers.values(),key=lambda v: -v['position']):
            if v['visible']: T.append(s2d.color(v['layer'],v['color']))
        T.append(s2d.color(self.cutout, (35,35,40)))
        return T


    def __iadd__(self, rhs):
        if isinstance(rhs, Component):
            self.components.append(rhs)
        elif isinstance(rhs, Connection):
            self.connections.append(rhs)
        else:
            raise TypeError("Invalid type for PCB addition (%s)" % type(rhs))
        return self

    def add_custom_layer(self,name,layer,color):
        self.custom_layers[name] = {'layer':layer,'color':color,'position':len(self.custom_layers),'visible':1}
    def hide_layer(self,name):
        self.custom_layers[name]['visible'] = 0

    def connectH(self, *args, **kwargs):
        ''' Connects a set of pins or points, traveling first
            horizontally then vertically
        '''
        width = kwargs['width'] if 'width' in kwargs else 0.016
        mode = kwargs['mode'] if 'mode' in kwargs else 'explicit'
        sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]]
        new_sides = []
        points = []
        args = list(args)
        for i,p in enumerate(args):
            if not isinstance(p,BoundPin):
                if mode=='diff':
                    args[i] = Point(args[i-1].x+p[0],args[i-1].y+p[1])
                elif mode=='explicit':
                    args[i] = Point(*p)
                else:
                    raise NotImplementedError("Unknown mode type %s"%mode)
        for A, B, s in zip(args[:-1], args[1:], sides):
            points.append(A); new_sides.append(s)
            if (A.x != B.x):
                points.append(Point(B.x, A.y)); new_sides.append(s)
        if A.y != B.y:  points.append(B)
        c = Connection(width, *points, sides=new_sides)
        self.connections.append(c)
        return c

    def connectV(self, *args, **kwargs):
        ''' Connects a set of pins or points, travelling first
            vertically then horizontally.
        '''
        width = kwargs['width'] if 'width' in kwargs else 0.016
        mode = kwargs['mode'] if 'mode' in kwargs else 'explicit'
        sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]]
        new_sides = []
        points = []
        args = list(args)
        for i,p in enumerate(args):
            if not isinstance(p,BoundPin):
                if mode=='diff':
                    args[i] = Point(args[i-1].x+p[0],args[i-1].y+p[1])
                elif mode=='explicit':
                    args[i] = Point(*p)
                else:
                    raise NotImplementedError("Unknown mode type %s"%mode)
        for A, B, s in zip(args[:-1], args[1:], sides):
            points.append(A); new_sides.append(s)
            if (A.y != B.y):
                points.append(Point(A.x, B.y)); new_sides.append(s)
        if A.x != B.x:  points.append(B)
        c = Connection(width, *points, sides=new_sides)
        self.connections.append(c)
        return c

    def connectD(self, *args, **kwargs):
        ''' Connects a set of pins or points, travelling first
            diagonally then horizontally or vertically, depending on geometry.
        '''
        width = kwargs['width'] if 'width' in kwargs else 0.016
        sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]]
        new_sides = []
        points = []
        def sgn(x): 
            if x>=0: 
                return 1 
            else:
                return -1
        args = list(args)
        for i,p in enumerate(args):
            if not isinstance(p,BoundPin):
                args[i] = Point(*p)
        for A, B, s in zip(args[:-1], args[1:], sides):
            points.append(A); new_sides.append(s)
            if (B.y-A.y != B.x-A.x):
                if abs(B.y-A.y) > abs(B.x-A.x):
                    points.append(Point(B.x, A.y+sgn(B.y-A.y)*abs(B.x-A.x))); new_sides.append(s)
                else:
                    points.append(Point(A.x+sgn(B.x-A.x)*abs(B.y-A.y),B.y)); new_sides.append(s)                    
        if (A.x != B.x) or (A.y != B.y):  points.append(B)
        c = Connection(width, *points, sides=new_sides)
        self.connections.append(c)
        return c


################################################################################

class Component(object):
    ''' Generic PCB component.
    '''
    def __init__(self, x, y, rot=0, name='',label_size=0.05, side=0):
        ''' Constructs a Component object
                x           X position
                y           Y position
                rotation    angle (degrees)
                name        String
                side        which side of board 0 for top, 1 for bottom
        '''
        self.x = x
        self.y = y
        self.rot   = rot
        self.name = name
        self.label_size = label_size
        self.side = side
        if self.side == 1:
            self.pins = [p.mirror_x()  for p in self.pins]
            self.vias = [v.mirror_x()  for v in self.vias]

    def __getitem__(self, i):
        if isinstance(i, str):
            try:
                pin = [p for p in self.pins if p.name == i][0]
            except IndexError:
                raise IndexError("No pin with name %s" % i)
        elif isinstance(i, int):
            try:
                pin = self.pins[i-1]
            except IndexError:
                raise IndexError("Pin %i is not in array" %i)
        return BoundPin(pin, self)

    @property
    def pads(self):
        pads = reduce(operator.add, [p.pad for p in self.pins])
        return s2d.move(s2d.rotate(pads, self.rot), self.x, self.y)

    @property
    def holes(self):
        if self.vias:
            holes = reduce(operator.add,[v.hole for v in self.vias])
            return s2d.move(s2d.rotate(holes,self.rot), self.x, self.y)
        else: return None

    @property
    def pin_labels(self):
        L = []
        for p in self.pins:
            p = BoundPin(p, self)
            if p.pin.name:
                t = s2d.rotate(text(p.pin.name, 0, 0, p.pin.label_size),self.rot+p.pin.label_rot)
                L.append(s2d.move(t, p.x, p.y))
        return reduce(operator.add, L) if L else None

    @property
    def label(self):
        return text(self.name, self.x, self.y, self.label_size)
    @property
    def shadow_shape(self):
        try:
            return s2d.move(s2d.rotate(self.shadow, self.rot),self.x, self.y)
        except AttributeError:
            return None

################################################################################

class Pin(object):
    ''' PCB pin, with name, shape, and position
    '''
    def __init__(self, x, y, shape, name='', label_size=.03, label_rot=0):
        self.x      = x
        self.y      = y
        self.shape  = shape
        self.name   = name
        self.label_size   = label_size
        self.label_rot   = label_rot

    @property
    def pad(self):
        return s2d.move(self.shape, self.x, self.y)

    def mirror_x(self):
        return Pin( -self.x, self.y, self.shape, self.name, label_size=self.label_size, label_rot=self.label_rot )

################################################################################

class Via(object):
    ''' PCB via, with shape, and position
    '''
    def __init__(self, x, y, shape):
        self.x      = x
        self.y      = y
        self.shape  = shape

    @property
    def hole(self):
        return s2d.move(self.shape, self.x, self.y)

    def mirror_x(self):
        return Via( -self.x, self.y, self.shape )

################################################################################

class BoundPin(object):
    ''' PCB pin localized to a specific component
        (so that it has correct x and y positions)
    '''
    def __init__(self, pin, component):
        self.pin = pin
        self.component = component

    @property
    def x(self):
        return (cos(radians(self.component.rot)) * self.pin.x -
                sin(radians(self.component.rot)) * self.pin.y +
                self.component.x)

    @property
    def y(self):
        return (sin(radians(self.component.rot)) * self.pin.x +
                cos(radians(self.component.rot)) * self.pin.y +
                self.component.y)
    @property
    def point(self):
        return Point(self.x,self.y)
################################################################################

class Point(object):
    ''' Object with x and y member variables
    '''
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __iter__(self):
        return iter([self.x, self.y])
    def __add__(self, p):
        return Point(self.x+p.x,self.y+p.y)
    def __sub__(self, p):
        return Point(self.x-p.x,self.y-p.y)
    def __rmul__(self,a):
        return Point(a*self.x,a*self.y)
    def magnitude(self):
        return sqrt(self.x*self.x + self.y+self.y)
    def normalized(self):
        return Point(self.x/self.magnitude(), self.y/self.magnitude())
    @property
    def point(self):
        return self

################################################################################

class Connection(object):
    ''' Connects two pins via a series of intermediate points
    '''
    def __init__(self, width, *args, **kwargs):
        self.width = width
        self.points = [
            a if isinstance(a, BoundPin) else Point(*a) for a in args
        ]
        self.sides = kwargs['sides'] if 'sides' in kwargs else [0 for a in args[:-1]] #0 is base side, 1 is other side
        self.holes = None
        self.jumpers = []
    
    def add_jumper(self,p,rot=0,width=.12, height=.07,thick=.05):
        self.jumpers.append((p,rot,width,height,thick))
        return self

    def cut_corners(self,idx):
        for i in idx:
            i,v = i #unpack index and distance
            assert(i>0) #start corner numbering at 1
            assert(i<len(self.points)) #no corner to cut at end
            d = lambda p,q: sqrt( (p.x-q.x)**2 + (p.y-q.y)**2 )
            dm = d(self.points[i],self.points[i-1])
            dp = d(self.points[i],self.points[i+1])
            #if dm > dp:
            self.points = self.points[:i] + \
                [Point(self.points[i].x-v/dm*(self.points[i].x-self.points[i-1].x ), self.points[i].y-v/dm*(self.points[i].y-self.points[i-1].y )),
                 Point(self.points[i].x+v/dp*(self.points[i+1].x-self.points[i].x ), self.points[i].y+v/dp*(self.points[i+1].y-self.points[i].y ))
                ] + \
                self.points[i+1:]
            self.sides.insert(i,self.sides[i])
            #else:
            #    self.points[i] = self.points[i] - dm/dp*(self.points[i]-self.points[i+1])
        return self

    @property
    def traces(self):
        #_pad_1206 = s2d.rectangle(-0.025, 0.025, -0.034, 0.034)
        _pad_via = s2d.circle(0,0,.025) #s2d.rectangle(-0.025, 0.025, -0.025, 0.025)
        _hole_via = s2d.circle(0,0,.016)
        jumper_cuts = []
        jumper_pads = []
        for p,r,w,h,t in self.jumpers:
            _jumper_pad  = s2d.move(s2d.rectangle(-.5*t, .5*t, -.5*h, .5*h),-.5*w,0)
            _jumper_pad += s2d.move(s2d.rectangle(-.5*t, .5*t, -.5*h, .5*h), .5*w,0)
            _cut = s2d.rectangle(-.5*w,.5*w,-.5*h,.5*h)
            jumper_cuts.append(s2d.move(s2d.rotate(_cut,r),p[0],p[1]))
            jumper_pads.append(s2d.move(s2d.rotate(_jumper_pad,r),p[0],p[1]))
        t = [[],[]]
        for p1, p2, side in zip(self.points[:-1], self.points[1:], self.sides):
            d = sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2)
            if p2 != self.points[-1]:
                d += self.width/2
            a = atan2(p2.y - p1.y, p2.x - p1.x)
            r = s2d.rounded_rectangle(0, d, -self.width/2, self.width/2,1.)
            t[side].append(s2d.move(s2d.rotate(r, degrees(a)), p1.x, p1.y))
        try:
            result0 = reduce(operator.add, t[0])
        except TypeError:
            result0 = None
        try:
            result1 = reduce(operator.add, t[1])
        except TypeError:
            result1 = None
        #calculate locations for via holes and pads
        for s1,s2,p in zip(self.sides[:-1],self.sides[1:],self.points[1:-1]):
            if s1!=s2:
                result0 += s2d.move(_pad_via,p.x,p.y)
                result1 += s2d.move(_pad_via,p.x,p.y)
                self.holes += s2d.move(_hole_via,p.x,p.y)
        if len(self.jumpers)!=0:
            result0 -= reduce(operator.add,jumper_cuts)
            result0 += reduce(operator.add,jumper_pads)
        return result0, result1

################################################################################
# Discrete passive components
################################################################################

_pad_1206 = s2d.rectangle(-0.032, 0.032, -0.034, 0.034)

class R_1206(Component):
    ''' 1206 Resistor
    '''
    pins = [Pin(-0.06, 0, _pad_1206), Pin(0.06, 0, _pad_1206)]
    prefix = 'R'
    vias = []


class C_1206(Component):
    ''' 1206 Capacitor
    '''
    pins = [Pin(-0.06, 0, _pad_1206), Pin(0.06, 0, _pad_1206)]
    prefix = 'C'
    vias = []

_pad_0805 = s2d.rectangle(-.023,.023, -.027, .027)

class R_0805(Component):
    ''' 0805 Resistor
    '''
    pins = [Pin(-0.04, 0, _pad_0805), Pin(0.04, 0, _pad_0805)]
    prefix = 'R'
    vias = []


class C_0805(Component):
    ''' 0805 Capacitor
    '''
    pins = [Pin(-0.04, 0, _pad_0805), Pin(0.04, 0, _pad_0805)]
    prefix = 'C'
    vias = []


_pad_SJ = s2d.rectangle(-0.02, 0.02, -0.03, 0.03)
class SJ(Component):
    ''' Solder jumper
    '''
    pins = [Pin(-0.029, 0, _pad_SJ), Pin(0.029, 0, _pad_SJ)]
    prefix = 'SJ'
    vias = []

_pad_SOD_123 = s2d.rectangle(-0.02, 0.02, -0.024, 0.024)
class D_SOD_123(Component):
    ''' Diode
    '''
    pins = [Pin(-0.07, 0, _pad_SOD_123, 'A'),
            Pin(0.07, 0, _pad_SOD_123, 'C')]
    prefix = 'D'
    vias = []


################################################################################
# Connectors
################################################################################

_pad_USB_trace = s2d.rectangle(-0.0075, 0.0075, -0.04, 0.04)
_pad_USB_foot  = s2d.rectangle(-0.049, 0.049, -0.043, 0.043)
class USB_mini_B(Component):
    ''' USB mini B connector
        Hirose UX60-MB-5ST
    '''
    pins = [
        Pin(0.063,   0.24, _pad_USB_trace, 'G'),
        Pin(0.0315,  0.24, _pad_USB_trace),
        Pin(0,       0.24, _pad_USB_trace, '+'),
        Pin(-0.0315, 0.24, _pad_USB_trace, '-'),
        Pin(-0.063,  0.24, _pad_USB_trace, 'V'),

        Pin( 0.165, 0.21, _pad_USB_foot),
        Pin(-0.165, 0.21, _pad_USB_foot),
        Pin( 0.165, 0.0, _pad_USB_foot),
        Pin(-0.165, 0.0, _pad_USB_foot)
    ]
    prefix = 'J'
    vias = []

_pad_header  = s2d.rectangle(-0.06, 0.06, -0.025, 0.025)
_pad_header_skinny  = s2d.rectangle(-0.06, 0.06, -0.020, 0.020)
class Header_4(Component):
    ''' 4-pin header
        fci 95278-101a04lf bergstik 2x2x0.1
    '''
    pins = [
        Pin(-0.107,  0.05, _pad_header),
        Pin(-0.107, -0.05, _pad_header),
        Pin( 0.107, -0.05, _pad_header),
        Pin( 0.107,  0.05, _pad_header)
    ]
    prefix = 'J'
    vias = []

class Header_4_skinny(Component):
    ''' 4-pin header
        fci 95278-101a04lf bergstik 2x2x0.1
    '''
    pins = [
        Pin(-0.107,  0.05, _pad_header_skinny),
        Pin(-0.107, -0.05, _pad_header_skinny),
        Pin( 0.107, -0.05, _pad_header_skinny),
        Pin( 0.107,  0.05, _pad_header_skinny)
    ]
    prefix = 'J'
    vias = []

class Header_Power(Component):
    ''' 4-pin header
        fci 95278-101a04lf bergstik 2x2x0.1
    '''
    pins = [
        Pin(-0.107,  0.05, _pad_header,"V"),
        Pin(-0.107, -0.05, _pad_header,"GND"),
        Pin( 0.107, -0.05, _pad_header),
        Pin( 0.107,  0.05, _pad_header)
    ]
    prefix = 'J'
    vias = []

class Header_ISP(Component):
    ''' ISP programming header
        FCI 95278-101A06LF Bergstik 2x3x0.1
    '''
    pins = [
        Pin(-0.107, 0.1,  _pad_header, 'GND'),
        Pin(-0.107, 0,    _pad_header, 'MOSI'),
        Pin(-0.107, -0.1, _pad_header, 'V'),
        Pin( 0.107, -0.1, _pad_header, 'MISO'),
        Pin( 0.107, 0,    _pad_header, 'SCK'),
        Pin( 0.107, 0.1,  _pad_header, 'RST')
    ]
    prefix = 'J'
    vias = []

class Header_ISP_skinny(Component):
    ''' ISP programming header
        FCI 95278-101A06LF Bergstik 2x3x0.1
    '''
    pins = [
        Pin(-0.107, 0.1,  _pad_header_skinny, 'GND'),
        Pin(-0.107, 0,    _pad_header_skinny, 'MOSI'),
        Pin(-0.107, -0.1, _pad_header_skinny, 'V'),
        Pin( 0.107, -0.1, _pad_header_skinny, 'MISO'),
        Pin( 0.107, 0,    _pad_header_skinny, 'SCK'),
        Pin( 0.107, 0.1,  _pad_header_skinny, 'RST')
    ]
    prefix = 'J'
    vias = []
    #shadow = s2d.rectangle(-.06,8/25.4,-.325,.325)


class Header_FTDI(Component):
    ''' FTDI cable header
    '''
    pins = [
        Pin(0,  0.25, _pad_header, 'GND'),
        Pin(0,  0.15, _pad_header, 'CTS'),
        Pin(0,  0.05, _pad_header, 'VCC'),
        Pin(0, -0.05, _pad_header, 'TX'),
        Pin(0, -0.15, _pad_header, 'RX'),
        Pin(0, -0.25, _pad_header, 'RTS')
    ]
    prefix = 'J'
    vias = []
    shadow = s2d.rectangle(-.06,8/25.4,-.325,.325)

class Header_FTDI_skinny(Component):
    ''' FTDI cable header
    '''
    pins = [
        Pin(0,  0.25, _pad_header_skinny, 'GND'),
        Pin(0,  0.15, _pad_header_skinny, 'CTS'),
        Pin(0,  0.05, _pad_header_skinny, 'VCC'),
        Pin(0, -0.05, _pad_header_skinny, 'TX'),
        Pin(0, -0.15, _pad_header_skinny, 'RX'),
        Pin(0, -0.25, _pad_header_skinny, 'RTS')
    ]
    prefix = 'J'
    vias = []
    shadow = s2d.rectangle(-.06,8/25.4,-.325,.325)


class ScrewTerminal(Component):
    pitch = .131
    _pad = s2d.rectangle(-0.04, 0.04, -0.04, 0.04)
    _via = s2d.circle(0,0,.025)  
    pins = [Pin(-.5*pitch,0,_pad),Pin(.5*pitch,0,_pad)]
    vias = [Via(-.5*pitch,0,_via),Via(.5*pitch,0,_via)]
    shadow = s2d.rectangle(-3.5/25.4,3.5/25.4,-3/25.4,3/25.4)

class ScrewTerminal3(Component):
    pitch = .131
    _pad = s2d.rectangle(-0.04, 0.04, -0.04, 0.04)
    _via = s2d.circle(0,0,.025)  
    pins = [Pin(-pitch,0,_pad),Pin(0,0,_pad),Pin(pitch,0,_pad)]
    vias = [Via(-pitch,0,_via),Via(0,0,_via),Via(pitch,0,_via)]
    shadow = s2d.rectangle(-5.35/25.4,5.35/25.4,-3/25.4,3/25.4)

class JST_2(Component):
    pitch = 2./25.4
    _pad = s2d.rectangle(-0.5/25.4,0.5/25.4, -1.75/25.4, 1.75/25.4)
    _pad2 = s2d.rectangle(-.75/25.4,.75/25.4,-1.7/25.4,1.7/25.4)
    y2 = -4.55/25.4
    pins = [Pin(-.5*pitch,0,_pad,'VCC'),Pin(.5*pitch,0,_pad,'GND'),Pin(-.5*pitch-2.35/25.4,y2,_pad2),Pin(.5*pitch+2.35/25.4,y2,_pad2)]
    vias = []
    shadow = s2d.rectangle(-3.95/25.4,3.95/25.4,y2-1.7/25.4,1.75/25.4)

################################################################################
# SOT-23 components
################################################################################

_pad_SOT23 = s2d.rectangle(-.02,.02,-.012,.012)
class NMOS_SOT23(Component):
    ''' NMOS transistor in SOT23 package
        Fairchild NDS355AN
    '''
    pins = [
        Pin(0.045, -0.0375, _pad_SOT23,'G'),
        Pin(0.045,  0.0375, _pad_SOT23,'S'),
        Pin(-0.045, 0, _pad_SOT23,'D')
    ]
    prefix = 'Q'
    vias = []

class PMOS_SOT23(Component):
    ''' PMOS transistor in SOT23 package
        Fairchild NDS356AP
    '''
    pins = [
        Pin(-0.045, -0.0375, _pad_SOT23,'G'),
        Pin(-0.045,  0.0375, _pad_SOT23,'S'),
        Pin(0.045, 0, _pad_SOT23,'D')
    ]
    prefix = 'Q'
    vias = []

class Regulator_SOT23(Component):
    '''  SOT23 voltage regulator
    '''
    pins = [
        Pin(-0.045, -0.0375, _pad_SOT23,'Out'),
        Pin(-0.045,  0.0375, _pad_SOT23,'In'),
        Pin(0.045, 0, _pad_SOT23,'GND')
    ]
    prefix = 'U'
    vias = []

class Regulator_LM3480(Component):
    '''  SOT23 voltage regulator, LM3480
    '''
    pins = [
        Pin(-0.045, -0.0375, _pad_SOT23,'In'),
        Pin(-0.045,  0.0375, _pad_SOT23,'Out'),
        Pin(0.045, 0, _pad_SOT23,'GND')
    ]
    prefix = 'U'
    vias = []

###########
# H Bridge
############
_pad_SOIC = s2d.rectangle(-.041,.041,-.015,.015)
class A4953_SOICN(Component):
    pins = [
        Pin(-.11, .075,_pad_SOIC+s2d.circle(-.041,0,.015),"GND"),
        Pin(-.11, .025,_pad_SOIC,"IN2"),
        Pin(-.11,-.025,_pad_SOIC,"IN1"),
        Pin(-.11,-.075,_pad_SOIC,"VREF"),
        Pin( .11,-.075,_pad_SOIC,"VBB"),
        Pin( .11,-.025,_pad_SOIC,"OUT1"),
        Pin( .11, .025,_pad_SOIC,"LSS"),
        Pin( .11, .075,_pad_SOIC,"OUT2"),
        Pin( 0,0,s2d.rectangle(-.04,.04,-.075,.075),"")
    ]
    prefix = 'U'
    vias = []


################################################################################
#   Clock crystals
################################################################################
_pad_XTAL_NX5032GA = s2d.rectangle(-.039,.039,-.047,.047)

class XTAL_NX5032GA(Component):
    pins = [Pin(-0.079, 0, _pad_XTAL_NX5032GA),
            Pin(0.079, 0, _pad_XTAL_NX5032GA)]
    prefix = 'X'
    vias = []

################################################################################
# Atmel microcontrollers
################################################################################

_pad_SOIC = s2d.rectangle(-0.041, 0.041, -0.015, 0.015)
class ATtiny45_SOIC(Component):
    pins = []
    y = 0.075
    for t in ['NC', 'PB3', 'PB4', 'GND']:
        pins.append(Pin(-0.14, y, _pad_SOIC, t))
        y -= 0.05
    for p in ['PB0', 'PB1', 'PB2', 'VCC']:
        y += 0.05
        pins.append(Pin(0.14, y, _pad_SOIC, p))
    del y
    prefix = 'U'
    vias = []

class ATtiny44_SOIC(Component):
    pins = []
    y = 0.15
    for t in ['VCC', 'PB0', 'PB1', 'PB3', 'PB2', 'PA7', 'PA6']:
        pad = _pad_SOIC + s2d.circle(-0.041, 0, 0.015) if t == 'VCC' else _pad_SOIC
        pins.append(Pin(-0.12, y, pad, t))
        y -= 0.05
    for t in ['PA5', 'PA4', 'PA3', 'PA2', 'PA1', 'PA0', 'GND']:
        y += 0.05
        pins.append(Pin(0.12, y, _pad_SOIC, t))
    prefix = 'U'
    vias = []

_pad_TQFP_h = s2d.rectangle(-0.025, 0.025, -0.008, 0.008)
_pad_TQFP_v = s2d.rectangle(-0.008, 0.008, -0.025, 0.025)

class ATmega88_TQFP(Component):
    pins = []
    y = 0.1085
    for t in ['PD3', 'PD4', 'GND', 'VCC', 'GND', 'VCC', 'PB6', 'PB7']:
        pins.append(Pin(-0.18, y, _pad_TQFP_h, t))
        y -= 0.031
    x = -0.1085
    for t in ['PD5', 'PD6', 'PD7', 'PB0', 'PB1', 'PB2', 'PB3', 'PB4']:
        pins.append(Pin(x, -0.18, _pad_TQFP_v, t))
        x += 0.031
    y = -0.1085
    for t in ['PB5', 'AVCC', 'ADC6', 'AREF', 'GND', 'ADC7', 'PC0', 'PC1']:
        pins.append(Pin(0.18, y, _pad_TQFP_h, t))
        y += 0.031
    x = 0.1085
    for t in ['PC2', 'PC3', 'PC4', 'PC5', 'PC6', 'PD0', 'PD1', 'PD2']:
        pins.append(Pin(x, 0.18, _pad_TQFP_v, t))
        x -= 0.031
    del x, y
    prefix = 'U'
    vias = []


################################################################################
#   CBA logo
################################################################################
_pin_circle_CBA = s2d.circle(0, 0, 0.02)
_pin_square_CBA = s2d.rectangle(-0.02, 0.02, -0.02, 0.02)
class CBA(Component):
    pins = []
    for i in range(3):
        for j in range(3):
            pin = _pin_circle_CBA if i == 2-j and j >= 1 else _pin_square_CBA
            pins.append(Pin(0.06*(i-1), 0.06*(j-1), pin))
    vias = []



class ESP8266_03(Component):
    _pad = s2d.rectangle(-0.04, 0.04, -0.03, 0.03)
    _via = s2d.circle(0,0,.019)
    names = ['VCC','GPIO14','GPIO12','GPIO13','GPIO15','GPIO2','GPIO0',
        'WIFI_ANT','CH-PD','GPIO18','URXD','UTXD','NC','GND']
    w = 12.2/25.4
    l = 17.4/25.4
    wp = 12.2/25.4
    lp = .5
    dp = 2/25.4
    ys = arange(.5*lp-dp    ,-.5*lp-.001-dp,-dp)
    pts = vstack(( dstack((-.5*wp*ones_like(ys),ys))[0], dstack((.5*wp*ones_like(ys),ys))[0] ))
    pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)]
    vias = []#[Via(p[0],p[1],_via) for n,p in zip(names,pts)]
    shadow = s2d.rectangle(-.5*w,.5*w,-.5*l,.5*l)
    prefix = 'IC'  

class ZLDO1117(Component):
    '''3.3 V 1 A regulator, SOT223'''
    _pad1 = s2d.rectangle(-.6/25.4,.6/25.4,-.8/25.4,.8/25.4)
    _pad2 = s2d.rectangle(-1.65/25.4,1.65/25.4,-.6/25.4,.6/25.4)
    pins = [
        Pin(-2.3/25.4, -3.2/25.4, _pad1,'GND'),
        Pin(0, -3.2/25.4, _pad1,'Vout'),
        Pin(2.3/25.4, -3.2/25.4, _pad1,'Vin'),
        Pin(0, 3.2/25.4, _pad2,'Vout2'),
    ]
    prefix = 'U'
    vias = []

class AstarMicro(Component):
    ''' Polulo Astar micro
    '''
    _pad = s2d.rectangle(-0.04, 0.04, -0.025, 0.025)
    _via = s2d.circle(0,0,.019)
    #flip names since through hole
    names = [
        'VIN','GND','5V','3v3','RST','12/A11/PWM','11','10/A10/PWM','A1','A0',
        '9/A9/PWM','8/A8','7','6/A7/PWM','5/PWM','4/A6','3/PWM','2','1','0']
    w = .6
    l = 1.
    wp = .5
    lp = .9
    ys = arange(.5*lp,-.5*lp-.001,-.1)
    os = 0*.13*(arange(shape(ys)[0])%2-.5)
    pts = vstack(( dstack((-.5*wp*ones_like(ys)+os,ys[::-1]))[0], dstack((.5*wp*ones_like(ys)-os,ys))[0] ))
    pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)]
    vias = [Via(p[0],p[1],_via) for n,p in zip(names,pts)]
    shadow = s2d.rectangle(-.5*w,.5*w,-.5*l,.5*l)
    prefix = 'IC'

class Header_bldc_skinny(Component):
    ''' brushless motor logic
    '''
    _pad_header_skinny  = s2d.rectangle(-0.06, 0.06, -0.020, 0.020)
    pins = [
        Pin(0,  0.1, _pad_header_skinny, 'GND'),
        Pin(0, -0.0, _pad_header_skinny, 'VCC'),
        Pin(0, -0.1, _pad_header_skinny, 'RC')
    ]
    prefix = 'J'
    shadow = s2d.rectangle(-.06,8/25.4,-.325,.325)
    vias = []


class A4988_Carrier(Component):
    '''  Stepper driver carrier black from pololu
    '''
    _pad = s2d.rectangle(-0.04, 0.04, -0.028, 0.028)
    _via = s2d.circle(0,0,.019)
    names = ['VMOT','GMOT','2B','2A','1A','1B','VDD','GND','DIR','STEP','SLP','RST','MS3','MS2','MS1','EN']
    ys = arange(.4,-.4+.001,-.1)-.05    
    pts = vstack(( dstack((-.25*ones_like(ys),ys))[0], dstack((.25*ones_like(ys),ys[::-1]))[0] ))
    pins = [Pin(p[0],p[1],_pad,n) for n,p in zip(names,pts)]
    vias = [Via(p[0],p[1],_via) for n,p in zip(names,pts)]
    prefix = 'IC'
    shadow = s2d.rectangle(-.3,.3,-.45,.45)




class CDRH2D18(Component):
    '''Power Inductor'''
    def chamfered_rectangle(x0,x1,y0,y1,c):
        r = s2d.rectangle(x0,x1,y0,y1)
        c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0)
        c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1)
        c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c)
        c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c)
        return r-c1-c2-c3-c4
    _pad = s2d.rectangle(-.65/25.4,.65/25.4,-.65/25.4,.65/25.4)
    pins = [Pin(-1.5/25.5,0,_pad), Pin(1.5/25.5,0,_pad)]
    vias = []
    shadow = s2d.rotate(chamfered_rectangle(-1.5/25.4,1.5/25.4,-1.5/25.4, 1.5/25.4,1/25.5),45)
    prefix='I'

class LTC35881(Component):
    ''' Energy Scavenger '''
    _pad  = s2d.rectangle(-.889/2/25.4, .889/2/25.4,-.25/2/25.4, .25/2/25.4)
    p = .5/25.4
    pins = [
        Pin(0, 0, s2d.rectangle(-1.68/2/25.4,1.68/2/25.4,-1.88/2/25.4,1.88/2/25.4), 'GND'),
        Pin(-2.1/25.4, 2*p,_pad,'PZ1',label_size=.015,label_rot=0),
        Pin(-2.1/25.4, 1*p,_pad,'PZ2',label_size=.015,label_rot=0),
        Pin(-2.1/25.4,   0,_pad,'CAP',label_size=.015,label_rot=0),
        Pin(-2.1/25.4,-1*p,_pad,'VIN',label_size=.015,label_rot=0),
        Pin(-2.1/25.4,-2*p,_pad,'SW',label_size=.015,label_rot=0),
        Pin(2.1/25.4, -2*p,_pad,'VOUT',label_size=.015,label_rot=0),
        Pin(2.1/25.4, -1*p,_pad,'VIN2',label_size=.015,label_rot=0),
        Pin(2.1/25.4,    0,_pad,'D1',label_size=.015,label_rot=0),
        Pin(2.1/25.4,  1*p,_pad,'D0',label_size=.015,label_rot=0),
        Pin(2.1/25.4,  2*p,_pad,'PGOOD',label_size=.015,label_rot=0)
    ]
    prefix = 'J'
    h = 2.9/25.4; w = 2.8/25.4;
    shadow = s2d.rectangle(-.5*w,.5*w,-.5*h,.5*h)
    vias = []    

class DSK414(Component):
    '''Dynacap, ELNA, 220mF'''
    pins = [
        Pin(0,5.15/25.4, s2d.rectangle(-2.4/25.4,2.4/25.4,-1./25.4,1/25.4),'+'),
        Pin(0,-5/25.4, s2d.rectangle(-2/25.4,2/25.4,-.85/25.4,.81/25.4),'-')
    ]
    vias = []
    shadow = s2d.rectangle(-2.5/25.4,2.5/25.4,-5.85/25.4, 6.15/25.4)
    shadow += s2d.circle(0,0,3.4/25.4)
    prefix='C'   

class EECRG(Component):
    '''Panasonic 1F, 3.6 V'''
    _pad = s2d.rectangle(-.02,.02,-.035,.035)
    pins = [
        Pin(-10/25.4, 0, _pad),
        Pin(10/25.4, 0, _pad)
    ]
    _via = s2d.rectangle(-.1/25.4,.1/25.4,-.5/25.4,.5/25.4)
    vias = [Via(p.x,p.y,_via) for p in pins]
    shadow = s2d.rectangle(0,0,0,0)
    prefix='C'

class EEE1EA101XP(Component):
    '''Panasonic 100uF, 25V'''
    _pad = s2d.rectangle(-.6/25.4,.6/25.4,-1.35/25.4,1.35/25.4)
    pins = [
        Pin(0, 2.2/25.4, _pad),
        Pin(0, -2.2/25.4, _pad)
    ]
    #_via = s2d.rectangle(-.1/25.4,.1/25.4,-.5/25.4,.5/25.4)
    vias = []
    def half_chamfered_rectangle(x0,x1,y0,y1,c):
        r = s2d.rectangle(x0,x1,y0,y1)
        c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0)
        c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1)
        c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c)
        c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c)
        return r-c1-c4
    shadow = half_chamfered_rectangle(-3.3/25.4,3.3/25.4,-3.3/25.4,3.3/25.4,1/25.4)
    prefix='C'


def chamfered_rectangle(x0,x1,y0,y1,c):
    r = s2d.rectangle(x0,x1,y0,y1)
    c1 = s2d.triangle(x0,y0,x0,y0+c,x0+c,y0)
    c2 = s2d.triangle(x1,y1, x1, y1-c, x1-c, y1)
    c3 = s2d.triangle(x0,y1, x0+c, y1, x0, y1-c)
    c4 = s2d.triangle(x1,y0, x1-c, y0, x1, y0+c)
    return r-c1-c2-c3-c4