class Meter(Group): """ A meter object as in Mike'n'Ike """ height = .7 width = 1.8 * height angle = 45 bg = Color(1) mcolor = Color(.8) def __init__(self, **args): Group.__init__(self, **args) h = self.height w = self.width self.append(Rectangle(width=1.8 * h, height=h, bg=self.bg)) p = Path(P(.1, .1), C(0, 0), P(w - .1, .1), P(w - .2, .1), C(0, 0), P(.2, .1), closed=1, bg=self.mcolor, fg=None) self.append( p, Path(P(w / 2., .1), U(self.angle, h * .9)), )
def CZGate(c=P(0, 0), controlDist=1.0, direction="up", side=0.5): """ Controlled Z gate @param controlDist: distance to the control @type controlDist: float @param direction: in which direction is the control? up/down @type direction: string @param side: length of the box side @type side: float """ if direction is "up": return Group( Circle(c=c + P(0, controlDist), r=0.065, bg=Color("black")), Path(c + P(0, side / 2.), c + P(0, controlDist)), Rectangle(width=side, height=side, c=c, bg=Color("white")), TeX(r'Z', c=c)) elif direction is "down": return Group( Circle(c=c - P(0, controlDist), r=0.65, bg=Color("black")), Path(c - P(0, side / 2.), c - P(0, controlDist)), Rectangle(width=side, height=side, c=c, bg=Color("white")), TeX(r'Z', c=c))
class Circled(Group, Circle): ''' Draws a circle around an object, @cvar pad: padding around object @cvar r: overide the radius of the circle ''' fg = Color(0) bg = Color(1) pad = 0.1 def __init__(self, obj, **options): Circle.__init__(self, **options) Group.__init__(self, **options) bbox = obj.bbox() w = bbox.width + 2 * self.pad h = bbox.height + 2 * self.pad self.r = options.get('r', max(w, h) / 2.) self.append( Circle(r=self.r, bg=self.bg, fg=self.fg, c=obj.c, linewidth=self.linewidth, dash=self.dash), obj, )
def __init__(self, **options): # initialise the base class Group.__init__(self, **options) self.height = 2.0 self.width = 3.0 self.angle = 0.0 self.pinLength = 0.5 self.fg = Color(0) self.bg = Color(1)
class LambdaPlate(Group): """ Lambda plate; shifts optical signal by a half or quarter wavelength @ivar height: height of the lambda plate @type height: float @ivar width: width of the lambda plate @type width: float @ivar angle: rotation angle @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 width = 0.3 angle = 0.0 fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) # make the beam splitter lp = Group() lp.append( Path(P(0, 0), P(-self.width, 0), P(-self.width, self.height), P(0, self.height), P(0, 0), P(-self.width, self.height), fg=self.fg, bg=self.bg)) # rotate if necessary lp.rotate(self.angle, p=lp.bbox().c) self.append(lp)
class FreeSpace(Group): """ A patch of free space (for example, in an interferometer) @ivar height: height of free space box @type height: float @ivar width: width of free space box (some might say "length") @type width: float @ivar angle: rotation angle @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 width = 3.0 angle = 0.0 fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) # make the free space fs = Group() fs.append( Path(P(0, 0), P(0, self.height), P(self.width, self.height), P(self.width, 0), closed=1, fg=self.fg, bg=self.bg, dash=Dash())) # rotate if necessary fs.rotate(self.angle, p=fs.bbox().c) self.append(fs)
class Laser(Group): """ Laser @ivar height: laser box height @type height: float @ivar width: laser box width (some might say "length") @type width: float @ivar angle: rotation angle @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 width = 3.0 angle = 0.0 fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) # make the laser laser = Group() laser.append( Path(P(0, 0), P(0, self.height), P(self.width, self.height), P(self.width, 0), closed=1, fg=self.fg, bg=self.bg)) # rotate if necessary laser.rotate(self.angle, p=laser.bbox().c) self.append(laser)
class PhaseShifter(Group): """ Phase shifter @ivar width: phase shifter width @type width: float @ivar height: phase shifter height @type height: float @ivar angle: angle through which to rotate the phase shifter @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ width = 0.5 height = 0.7 angle = 0 fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from base class Group.__init__(self, **options) # process the options if any self.width = options.get("width", self.width) self.height = options.get("height", self.height) self.angle = options.get("angle", self.angle) self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) # now make the phase shifter ps = Path( P(0, 0), P(self.width / 2.0, self.height), P(self.width, 0), closed=1, fg=self.fg, bg=self.bg, ) # rotate if necessary if self.angle != 0: ps.rotate(self.angle, p=ps.bbox().c) self.append(ps)
class BSLine(Group): """ Beam splitter as a line (i.e. a half-slivered mirror) @ivar height: height of the beam splitter @type height: float @ivar thickness: thickness of the beam splitter @type thickness: float @ivar angle: rotation angle @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 thickness = 0.2 angle = 45.0 fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.thickness = options.get("thickness", self.thickness) self.angle = options.get("angle", self.angle) # make the beam splitter bs = Group() bs.append( Path(P(0, 0), P(0, self.height), P(self.thickness, self.height), P(self.thickness, 0), closed=1, fg=self.fg, bg=self.bg)) # rotate if necessary bs.rotate(self.angle, p=bs.bbox().c) self.append(bs)
def classicalpath(*paths): ''' @return: classical path @param paths: 1 or more Path() objects ''' g = Group() for path in paths: g.append(path.copy(linewidth=2, fg=Color(0))) # reuse these paths for path in paths: g.append(path(linewidth=1, fg=Color(1))) return g
class BSBox(Group): """ Beam splitter as a box as opposed to a line @ivar height: height of the beam splitter (equal to its width) @type height: C{float} @ivar angle: rotation angle @type angle: C{float} @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 angle = 0.0 # not going to be used much (maybe for a Ralph-splitter ;-)) fg = Color(0) bg = Color(1) def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.angle = options.get("angle", self.angle) # make the beam splitter bs = Group() bs.append( Path(P(0, 0), P(0, self.height), P(self.height, self.height), P(self.height, 0), P(0, 0), P(self.height, self.height), fg=self.fg, bg=self.bg)) # rotate if necessary bs.rotate(self.angle, p=bs.bbox().c) self.append(bs)
class Detector(Group): ''' A D shaped detector, can be given an object to surround ''' height = .8 width = height / 2. bg = Color(1) fg = Color(0) pad = .1 def __init__(self, object=None, **options): if object is not None: # use the object's boundingbox when width and height not supplied bb = object.bbox() w = bb.width + 2 * self.pad h = bb.height + 2 * self.pad self.width = options.get("width", max(w, self.width)) self.height = options.get("height", max(h, self.height)) Group.__init__(self, **options) if self.width > self.height: p = Path(P(0, 0), P(0, self.height), P(self.width - self.height / 2., self.height), C(90, 0), P(self.width, self.height / 2.), C(180, 90), P(self.width - self.height / 2., 0), closed=1) else: p = Path(P(0, 0), P(0, self.height), C(90, 0), P(self.width, self.height / 2.), C(180, 90), closed=1) p(bg=options.get("bg", self.bg), fg=options.get("fg", self.fg)) self.append(p) if object is not None: # object looks better if it's slightly off centre # since one side is curved. pad/3 is about right object.c = P(self.width / 2. - self.pad / 3., self.height / 2.) self.append(object)
def BS(sw=P(0, 0), label=None, h=1.0): """ Beam splitter; displayed as a line possibly more useful in linear optics quantum computation diagrams @param sw: location of the south-west corner of the object @type sw: L{P} object @param label: beam splitter label @type label: string @param h: beam splitter height @type h: float """ buff = P(0, 0.1) b = Path(sw - buff, sw + P(0, h) + buff, sw + P(h, h) + buff, sw + P(h, 0) - buff, sw - buff, fg=None, bg=Color("white")) p1 = Path(sw, sw + P(h, h)) p2 = Path(sw + P(0, h), sw + P(h, 0)) p3 = Path(sw + P(h / 4, h / 2), sw + P(h, 0) + P(-h / 4, h / 2), linewidth=1) if label is not None: label['w'] = sw + P(h, 0) + P(-h / 4, h / 2) return Group(b, p1, p2, p3, label) else: return Group(b, p1, p2, p3)
def __init__(self, **options): # intitialise base class Group.__init__(self, **options) self.sep = 0.25 self.width = 1.0 self.angle = 0.0 self.pinLength = 0.5 self.fg = Color(0) self.bg = Color(1) # process the options if any self.sep = options.get("sep", self.sep) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) self.pinLength = options.get("pinLength", self.pinLength) self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) pinIn = Group( Path( P(0, 0), P(self.pinLength, 0), ) ) cap = Group( Path(pinIn.e+P(0, -self.width/2.0), pinIn.e+P(0, self.width/2.0)), Path(pinIn.e+P(self.sep, -self.width/2.0), pinIn.e+P(self.sep, self.width/2.0)), ) pinOut = Path( cap.e, cap.e+P(self.pinLength, 0)) # group the objects together obj = Group(pinIn, pinOut, cap) # apply the colours obj.apply(fg=self.fg, bg=self.bg) # rotate if necessary if self.angle != 0.0: obj.rotate(self.angle, p=obj.c) # set the object to myself self.append(obj)
class Box(Group, Rectangle): ''' Draws a box around an object, the box can be placed acording to standard Area tags @cvar pad: padding around object @cvar width: overide the width of the box @cvar height: override the height of the box ''' # set these preferences different from Rectangle: fg = Color(0) bg = Color(1) pad = .2 width = None height = None def __init__(self, obj, **options): Rectangle.__init__(self, **options) Group.__init__(self, **options) bbox = obj.bbox() self.object = obj w = bbox.width + 2 * self.pad h = bbox.height + 2 * self.pad # overide the width and height if supplied if self.width is None: self.width = options.get('width', w) if self.height is None: self.height = options.get('height', h) self.append( Rectangle(width=self.width, height=self.height, bg=self.bg, fg=self.fg, c=obj.c, r=self.r, linewidth=self.linewidth, dash=self.dash), obj, )
def Cnot(c=P(0, 0), targetDist=1.0, direction="up"): """ Controlled NOT gate @param targetDist: distance to the target rail @type targetDist: float @param direction: in which direction is the target rail? up/down @type direction: string """ if direction is "up": return Group(Circle(r=0.06, bg=Color("black"), c=c), Circle(r=0.2, c=c + P(0, targetDist)), Path(c, c + P(0, targetDist + 0.2))) elif direction is "down": return Group(Circle(r=0.06, bg=Color("black"), c=c), Circle(r=0.2, c=c + P(0, -targetDist)), Path(c, c + P(0, -targetDist - 0.2)))
def ZGate(c=P(0, 0), side=0.5): """ Z gate @param side: length of the box side @type side: float """ return Group(Rectangle(width=side, height=side, c=c, bg=Color("white")), TeX(r'Z', c=c))
class Boxed(Group, Rectangle): ''' Draws a box around an object, the box can be placed acording to standard Area tags @cvar pad: padding around object @type pad: float @cvar width: overide the width of the box @type width: float @cvar height: override the height of the box @type height: float ''' fg = Color(0) bg = Color(1) pad = 0.2 def __init__(self, obj, **options): Rectangle.__init__(self, **options) Group.__init__(self, **options) bbox = obj.bbox() w = bbox.width + 2 * self.pad h = bbox.height + 2 * self.pad self.width = options.get('width', w) self.height = options.get('height', h) self.append( Rectangle(width=self.width, height=self.height, bg=self.bg, fg=self.fg, c=obj.c, r=self.r, linewidth=self.linewidth, dash=self.dash), obj, )
def __init__(self, **options): # intitialise base class Group.__init__(self, **options) self.length = 3.0 self.width = 1.0 self.angle = 0.0 self.pinLength = 0.5 self.fg = Color(0) self.bg = Color(1) # process the options if any self.length = options.get("length", self.length) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) self.pinLength = options.get("pinLength", self.pinLength) self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) pinIn = Group( Path( P(0, 0), P(self.pinLength, 0) ) ) resistor = Rectangle(w=pinIn.e, width=self.length, height=self.width) pinOut = Path( resistor.e, resistor.e+P(self.pinLength, 0)) # collect the objects together obj = Group(pinIn, pinOut, resistor) # apply the colours obj.apply(fg=self.fg, bg=self.bg) # rotate if necessary if self.angle != 0.0: obj.rotate(self.angle, p=obj.c) # return object to myself self.append(obj)
class Poster(Page, VAlign): ''' A poster class @cvar size: the size of the poster eg A0 @cvar orientation: portrait or landscape @cvar space: space between vertically aligned objects appended to poster @cvar topspace: initial space at top of poster @cvar bg: background color of poster (unless background() method is overiden) ''' size = "A0" orientation = "portrait" bg = Color('DarkSlateBlue') space = 1 topspace = 2 def __init__(self, *objects, **options): Page.__init__(self, **options) VAlign.__init__(self, **options) back = self.background() # use Page's append so background doesn't get aligned Page.append(self, back) # add invisible area at top to start alignment a = Area(width=0, height=self.topspace - self.space, n=self.area().n) self.append(a) def background(self): ''' Return background for poster ''' area = self.area() signature = Text( 'Created with PyScript. http://pyscript.sourceforge.net', size=14, fg=Color(1)) signature.se = area.se + P(-.5, .5) return Group( Rectangle(width=area.width, height=area.height, fg=None, bg=self.bg), signature, )
def background(self): ''' Return background for poster ''' area = self.area() signature = Text( 'Created with PyScript. http://pyscript.sourceforge.net', size=14, fg=Color(1)) signature.se = area.se + P(-.5, .5) return Group( Rectangle(width=area.width, height=area.height, fg=None, bg=self.bg), signature, )
class TeXArea(Group): ''' Typeset some LaTeX within a fixed width minipage environment. @cvar width: the width of the environment @type width: float @cvar iscale: initial scale of the tex @type iscale: float @cvar align: alignment of the LaTeX to box if it iss smaller than the full width @type align: string (anchor point) @cvar fg: color of TeX @type fg: L{Color} object ''' # has to be different from groups width attribute width = 9.4 iscale = 1 fg = Color(0) align = "w" def __init__(self, text, **options): Group.__init__(self, **options) # set up tex width ... this relies on latex notion of # a point being accurate ... adjust for tex_scale too width_pp = int(self.width / float(self.iscale) * defaults.units) t = TeX(r'\begin{minipage}{%dpt}%s\end{minipage}' % (width_pp, text), fg=self.fg, iscale=self.iscale) # use this for alignment as the latex bounding box may be smaller # than the full width a = Area(width=self.width, height=0) Align(t, a, a1=self.align, a2=self.align, space=0) self.append(a, t)
def detector(**options): ''' @return: a D shaped detector ''' r = 0.3 c = 0.65 * r path = [ P(0, -r), P(0, r), C(P(c, r), P(r, c)), P(r, 0), C(P(r, -c), P(c, -r)), P(0, -r) ] options['bg'] = options.get('bg', Color(.8)) options['closed'] = 1 p = apply(Path, path, options) a = Area(width=r, height=2 * r, e=P(0, 0)) return Group(a, p)
class QWire(NoWire): """ Class representing a quantum wire """ fg = Color(0) linewidth = None dash = None def set(self, y, e, w): """ Set the east, west and y postions of the QWire """ path = Path(P(w, y), P(e, y), fg=self.fg, linewidth=self.linewidth, dash=self.dash) self.append(path) return self
def cbox(obj, x, yt, yc): ''' @param obj: the object to put a box around @type obj: object @param x: x position of line and centre of box @type x: float @param yt: y position of target @type yt: float @param yc: y position of control @type yc: float @return: a controlled box ''' g = Group( Path(P(x, yt), P(x, yc)), Boxed(obj, c=P(x, yt), bg=Color(1)), Dot(P(x, yc)), ) return g
def __init__(self, obj, **options): bbox = obj.bbox() pad = .1 r = max(bbox.width + 2 * pad, bbox.height + 2 * pad) / 2.0 self.width = 2.0 * r self.height = 2.0 * r self.bg = options.get('bg', Color(1)) if options.has_key('bg'): del options['bg'] apply(Group.__init__, (self, ), options) apply(Area.__init__, (self, ), options) obj.c = P(r, r) self.append( Circle(r=r, bg=self.bg, c=P(r, r)), obj, )
def __init__(self, obj, **options): bbox = obj.bbox() pad = .1 w = bbox.width + 2 * pad h = bbox.height + 2 * pad self.width = w self.height = h self.bg = options.get('bg', Color(1)) if options.has_key('bg'): del options['bg'] apply(Group.__init__, (self, ), options) apply(Area.__init__, (self, ), options) obj.c = P(w / 2., h / 2.) self.append( Rectangle(width=w, height=h, bg=self.bg), obj, )
class Detector(Group): """ A D-shaped detector @cvar height: detector height @type height: float @cvar width: detector width @type width: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object @cvar pad: space padding around object @type pad: float @ivar angle: rotation angle @type angle: float """ height = 0.8 width = height / 2.0 bg = Color(1) fg = Color(0) pad = 0.1 angle = 0.0 def __init__(self, **options): Group.__init__(self, **options) p = Group() self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) if self.width > self.height: p.append( Path(P(0, 0), P(0, self.height), P(self.width - self.height / 2.0, self.height), C(90, 0), P(self.width, self.height / 2.0), C(180, 90), P(self.width - self.height / 2.0, 0), fg=self.fg, bg=self.bg, closed=1)) else: p.append( Path(P(0, 0), P(0, self.height), C(90, 0), P(self.width, self.height / 2.0), C(180, 90), closed=1)) # rotate if necessary self.angle = options.get("angle", self.angle) p.rotate(self.angle, p=p.bbox().c) self.append(p)
class Modulator(Group): """ Modulator (EOM, AOM etc.) @ivar height: modulator box height @type height: float @ivar width: modulator box width @type width: float @ivar angle: rotation angle @type angle: float @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 0.5 width = 1.0 angle = 0.0 fg = Color(0) bg = Color(1) buf = height * 0.2 def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.width = options.get("width", self.width) self.angle = options.get("angle", self.angle) # make the modulator modulator = Group() modulator.append( Path( P(0, 0), P(0, self.height), P(self.width, self.height), P(self.width, 0), closed=1, fg=self.fg, bg=self.bg, )) modulator.append( Path( P(0, -self.buf), P(self.width, -self.buf), fg=self.fg, bg=self.bg, )) modulator.append( Path( P(0, self.height + self.buf), P(self.width, self.height + self.buf), fg=self.fg, bg=self.bg, )) # rotate if necessary modulator.rotate(self.angle, p=modulator.bbox().c) self.append(modulator)
class Lens(Group): """ A lens @ivar height: lens height @type height: float @ivar thickness: lens thickness @type thickness: float @ivar angle: rotation angle @type angle: float @ivar type: the type of lens: convex/concave @type type: string @ivar fg: foreground colour @type fg: L{Color} object @ivar bg: background colour @type bg: L{Color} object """ height = 1.0 thickness = 0.4 angle = 0.0 fg = Color(0) bg = Color(1) type = "concave" def __init__(self, **options): # inherit from the base class Group.__init__(self, **options) # process the options if any self.fg = options.get("fg", self.fg) self.bg = options.get("bg", self.bg) self.height = options.get("height", self.height) self.thickness = options.get("thickness", self.thickness) self.angle = options.get("angle", self.angle) self.type = options.get("type", self.type) # determine what type of lens to make if self.type == "convex": leftCurveAngle = -30 rightCurveAngle = -30 elif self.type == "concave": leftCurveAngle = 30 rightCurveAngle = 30 else: print "Unknown lens type, defaulting to concave" leftCurveAngle = 30 rightCurveAngle = 30 # make the lens lens = Group() lens.append( Path( P(0, 0), C(leftCurveAngle, 180 - leftCurveAngle), P(0, self.height), P(self.thickness, self.height), C(-180 + rightCurveAngle, -rightCurveAngle), P(self.thickness, 0), closed=1, fg=self.fg, bg=self.bg, )) # rotate if necessary lens.rotate(self.angle, p=lens.bbox().c) self.append(lens)