# Copyright (C) 2002-2006 Alexei Gilchrist and Paul Cochrane
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# $Id: optics.py,v 1.16 2006/04/24 14:24:26 paultcochrane Exp $
'''
PyScript optics objects library
'''
__revision__ = '$Revision: 1.16 $'
from pyscript import Group,Path,Color,P,C,Dash
# beam splitter
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)
# box beam splitter (aka polarising beam splitter)
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)
# polarising beam splitter
PBS = BSBox
# line beam splitter
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)
# phase shifter
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)
# mirror
class Mirror(Group):
"""
Mirror
@ivar length: mirror length
@type length: float
@ivar thickness: mirror thickness
@type thickness: float
@ivar angle: rotation angle
@type angle: float
@ivar flicks: put the mirror flicks on? (shows where back of mirror is)
@type flicks: boolean
@ivar fg: foreground colour
@type fg: L{Color} object
@ivar bg: background colour
@type bg: L{Color} object
"""
length = 1.0
thickness = 0.1
angle = 0.0
fg = Color(0)
bg = Color(0)
flicks = False
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.length = options.get("length", self.length)
self.thickness = options.get("thickness", self.thickness)
self.angle = options.get("angle", self.thickness)
self.flicks = options.get("flicks", self.flicks)
# make the mirror itself
mirror = Group()
mirror.append(
Path(P(0, 0),
P(0, self.length),
P(self.thickness, self.length),
P(self.thickness, 0),
closed=1,
fg=self.fg, bg=self.bg)
)
if self.flicks:
# make the flicks on the back of the mirror
flickLen = 0.15
flicksObj = Group()
for i in range(10):
flicksObj.append(
Path(
P((i+1.0)*self.length/10.0, self.thickness),
P(i*self.length/10.0, self.thickness+flickLen),
fg=self.fg, bg=self.bg
))
mirror.append(flicksObj)
# rotate the mirror if necessary
if self.angle != 0.0:
mirror.rotate(self.angle, p=mirror.bbox().c)
# make the mirror the current object
self.append(mirror)
# detector
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)
# laser (this is just a container, in case we want to make this fancier later)
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)
# modulator
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)
# free space
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)
# lens
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)
# lambda plate; shifts signal by a half or quarter wavelength
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)
# vim: expandtab shiftwidth=4:
|