signature:
def bezier_arc ( x1 , y1 , x2 , y2 , start_angle = 0 , extent = 90 ) :
    """Compute a cubic Bezier approximation of an elliptical arc .
 ( x1 , y1 ) and ( x2 , y2 ) are the corners of the enclosing rectangle .
 The coordinate system has coordinates that increase to the right and down .
 Angles , measured in degress , start with 0 to the right ( the positive X axis )
 and increase counter - clockwise .
 The arc extends from start _ angle to start _ angle + extent .
 I . e . start _ angle = 0 and extent = 180 yields an openside - down semi - circle .
 The resulting coordinates are of the form ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4)
 such that the curve goes from ( x1 , y1 ) to ( x4 , y4 ) with ( x2 , y2 ) and
 ( x3 , y3 ) as their respective Bezier control points ."""

implementation:
x1 , y1 , x2 , y2 = min ( x1 , x2 ) , max ( y1 , y2 ) , max ( x1 , x2 ) , min ( y1 , y2 )
if abs ( extent ) <= 90 :
    frag_angle = float ( extent )
    nfrag = 1
else :
    nfrag = int ( ceil ( abs ( extent ) / 90. ) )
    if nfrag == 0 :
        warnings . warn ( 'Invalid value for extent: %r' % extent )
        return [ ]
    frag_angle = float ( extent ) / nfrag
x_cen = ( x1 + x2 ) / 2.
y_cen = ( y1 + y2 ) / 2.
rx = ( x2 - x1 ) / 2.
ry = ( y2 - y1 ) / 2.
half_angle = radians ( frag_angle ) / 2
kappa = abs ( 4. / 3. * ( 1. - cos ( half_angle ) ) / sin ( half_angle ) )
if frag_angle < 0 :
    sign = - 1
else :
    sign = 1
point_list = [ ]
for i in range ( nfrag ) :
    theta0 = radians ( start_angle + i * frag_angle )
    theta1 = radians ( start_angle + ( i + 1 ) * frag_angle )
    c0 = cos ( theta0 )
    c1 = cos ( theta1 )
    s0 = sin ( theta0 )
    s1 = sin ( theta1 )
    if frag_angle > 0 :
        signed_kappa = - kappa
    else :
        signed_kappa = kappa
    point_list . append ( ( x_cen + rx * c0 , y_cen - ry * s0 , x_cen + rx * ( c0 + signed_kappa * s0 ) , y_cen - ry * ( s0 - signed_kappa * c0 ) , x_cen + rx * ( c1 - signed_kappa * s1 ) , y_cen - ry * ( s1 + signed_kappa * c1 ) , x_cen + rx * c1 , y_cen - ry * s1 ) )
return point_list


signature:
def angle ( x1 , y1 , x2 , y2 ) :
    """The angle in degrees between two vectors ."""

implementation:
sign = 1.0
usign = ( x1 * y2 - y1 * x2 )
if usign < 0 :
    sign = - 1.0
num = x1 * x2 + y1 * y2
den = hypot ( x1 , y1 ) * hypot ( x2 , y2 )
ratio = min ( max ( num / den , - 1.0 ) , 1.0 )
return sign * degrees ( acos ( ratio ) )


signature:
def transform_from_local ( xp , yp , cphi , sphi , mx , my ) :
    """Transform from the local frame to absolute space ."""

implementation:
x = xp * cphi - yp * sphi + mx
y = xp * sphi + yp * cphi + my
return ( x , y )


signature:
def elliptical_arc_to ( x1 , y1 , rx , ry , phi , large_arc_flag , sweep_flag , x2 , y2 ) :
    """An elliptical arc approximated with Bezier curves or a line segment .
 Algorithm taken from the SVG 1.1 Implementation Notes :
 http : / / www . w3 . org / TR / SVG / implnote . html # ArcImplementationNotes"""

implementation:
# Basic normalization .
rx = abs ( rx )
ry = abs ( ry )
phi = phi % 360
# Check for certain special cases .
if x1 == x2 and y1 == y2 : # Omit the arc .
# x1 and y1 can obviously remain the same for the next segment .
    return [ ]
if rx == 0 or ry == 0 : # Line segment .
    return [ ( x2 , y2 ) ]
rphi = radians ( phi )
cphi = cos ( rphi )
sphi = sin ( rphi )
# Step 1 : Rotate to the local coordinates .
dx = 0.5 * ( x1 - x2 )
dy = 0.5 * ( y1 - y2 )
x1p = cphi * dx + sphi * dy
y1p = - sphi * dx + cphi * dy
# Ensure that rx and ry are large enough to have a unique solution .
lam = ( x1p / rx ) ** 2 + ( y1p / ry ) ** 2
if lam > 1.0 :
    scale = sqrt ( lam )
    rx *= scale
    ry *= scale
# Step 2 : Solve for the center in the local coordinates .
num = max ( ( rx * ry ) ** 2 - ( rx * y1p ) ** 2 - ( ry * x1p ) ** 2 , 0.0 )
den = ( ( rx * y1p ) ** 2 + ( ry * x1p ) ** 2 )
a = sqrt ( num / den )
cxp = a * rx * y1p / ry
cyp = - a * ry * x1p / rx
if large_arc_flag == sweep_flag :
    cxp = - cxp
    cyp = - cyp
# Step 3 : Transform back .
mx = 0.5 * ( x1 + x2 )
my = 0.5 * ( y1 + y2 )
# Step 4 : Compute the start angle and the angular extent of the arc .
# Note that theta1 is local to the phi - rotated coordinate space .
dx = ( x1p - cxp ) / rx
dy = ( y1p - cyp ) / ry
dx2 = ( - x1p - cxp ) / rx
dy2 = ( - y1p - cyp ) / ry
theta1 = angle ( 1 , 0 , dx , dy )
dtheta = angle ( dx , dy , dx2 , dy2 )
if not sweep_flag and dtheta > 0 :
    dtheta -= 360
elif sweep_flag and dtheta < 0 :
    dtheta += 360
# Step 5 : Break it apart into Bezier arcs .
p = [ ]
control_points = bezier_arc ( cxp - rx , cyp - ry , cxp + rx , cyp + ry , theta1 , dtheta )
for x1p , y1p , x2p , y2p , x3p , y3p , x4p , y4p in control_points : # Transform them back to asbolute space .
    p . append ( ( transform_from_local ( x2p , y2p , cphi , sphi , mx , my ) + transform_from_local ( x3p , y3p , cphi , sphi , mx , my ) + transform_from_local ( x4p , y4p , cphi , sphi , mx , my ) ) )
return p


signature:
def _create_view ( self , name = "shoebot-output" ) :
    """Create the gtk . TextView used for shell output"""

implementation:
view = Gtk . TextView ( )
view . set_editable ( False )
fontdesc = Pango . FontDescription ( "Monospace" )
view . modify_font ( fontdesc )
view . set_name ( name )
buff = view . get_buffer ( )
buff . create_tag ( 'error' , foreground = 'red' )
return view


signature:
def set_bot ( self , bot ) :
    '''Bot must be set before running'''

implementation:
self . bot = bot
self . sink . set_bot ( bot )


signature:
def settings ( self , ** kwargs ) :
    '''Pass a load of settings into the canvas'''

implementation:
for k , v in kwargs . items ( ) :
    setattr ( self , k , v )


signature:
def size_or_default ( self ) :
    '''If size is not set , otherwise set size to DEFAULT _ SIZE
 and return it .
 This means , only the first call to size ( ) is valid .'''

implementation:
if not self . size :
    self . size = self . DEFAULT_SIZE
return self . size


signature:
def set_size ( self , size ) :
    '''Size is only set the first time it is called
 Size that is set is returned'''

implementation:
if self . size is None :
    self . size = size
    return size
else :
    return self . size


signature:
def snapshot ( self , target , defer = True , file_number = None ) :
    '''Ask the drawqueue to output to target .
 target can be anything supported by the combination
 of canvas implementation and drawqueue implmentation .
 If target is not supported then an exception is thrown .'''

implementation:
output_func = self . output_closure ( target , file_number )
if defer :
    self . _drawqueue . append ( output_func )
else :
    self . _drawqueue . append_immediate ( output_func )


signature:
def flush ( self , frame ) :
    '''Passes the drawqueue to the sink for rendering'''

implementation:
self . sink . render ( self . size_or_default ( ) , frame , self . _drawqueue )
self . reset_drawqueue ( )


signature:
def overlap ( self , x1 , y1 , x2 , y2 , r = 5 ) :
    """Returns True when point 1 and point 2 overlap .
 There is an r treshold in which point 1 and point 2
 are considered to overlap ."""

implementation:
if abs ( x2 - x1 ) < r and abs ( y2 - y1 ) < r :
    return True
else :
    return False


signature:
def reflect ( self , x0 , y0 , x , y ) :
    """Reflects the point x , y through origin x0 , y0."""

implementation:
rx = x0 - ( x - x0 )
ry = y0 - ( y - y0 )
return rx , ry


signature:
def angle ( self , x0 , y0 , x1 , y1 ) :
    """Calculates the angle between two points ."""


signature:
def coordinates ( self , x0 , y0 , distance , angle ) :
    """Calculates the coordinates of a point from the origin ."""

implementation:
x = x0 + cos ( radians ( angle ) ) * distance
y = y0 + sin ( radians ( angle ) ) * distance
return Point ( x , y )


signature:
def contains_point ( self , x , y , d = 2 ) :
    """Returns true when x , y is on the path stroke outline ."""

implementation:
if self . path != None and len ( self . path ) > 1 and self . path . contains ( x , y ) : # If all points around the mouse are also part of the path ,
# this means we are somewhere INSIDE the path .
# Only points near the edge ( i . e . on the outline stroke )
# should propagate .
    if not self . path . contains ( x + d , y ) or not self . path . contains ( x , y + d ) or not self . path . contains ( x - d , y ) or not self . path . contains ( x , y - d ) or not self . path . contains ( x + d , y + d ) or not self . path . contains ( x - d , y - d ) or not self . path . contains ( x + d , y - d ) or not self . path . contains ( x - d , y + d ) :
        return True
return False


signature:
def insert_point ( self , x , y ) :
    """Inserts a point on the path at the mouse location .
 We first need to check if the mouse location is on the path .
 Inserting point is time intensive and experimental ."""

implementation:
try :
    bezier = _ctx . ximport ( "bezier" )
except :
    from nodebox . graphics import bezier
# Do a number of checks distributed along the path .
# Keep the one closest to the actual mouse location .
# Ten checks works fast but leads to imprecision in sharp corners
# and curves closely located next to each other .
# I prefer the slower but more stable approach .
n = 100
closest = None
dx0 = float ( "inf" )
dy0 = float ( "inf" )
for i in range ( n ) :
    t = float ( i ) / n
    pt = self . path . point ( t )
    dx = abs ( pt . x - x )
    dy = abs ( pt . y - y )
    if dx + dy <= dx0 + dy0 :
        dx0 = dx
        dy0 = dy
        closest = t
# Next , scan the area around the approximation .
# If the closest point is located at 0.2 on the path ,
# we need to scan between 0.1 and 0.3 for a better
# approximation . If 1.5 was the best guess , scan
# 1.40 , 1.41 . . . 1.59 and so on .
# Each decimal precision takes 20 iterations .
decimals = [ 3 , 4 ]
for d in decimals :
    d = 1.0 / pow ( 10 , d )
    for i in range ( 20 ) :
        t = closest - d + float ( i ) * d * 0.1
        if t < 0.0 :
            t = 1.0 + t
        if t > 1.0 :
            t = t - 1.0
        pt = self . path . point ( t )
        dx = abs ( pt . x - x )
        dy = abs ( pt . y - y )
        if dx <= dx0 and dy <= dy0 :
            dx0 = dx
            dy0 = dy
            closest_precise = t
    closest = closest_precise
# Update the points list with the inserted point .
p = bezier . insert_point ( self . path , closest_precise )
i , t , pt = bezier . _locate ( self . path , closest_precise )
i += 1
pt = PathElement ( )
pt . cmd = p [ i ] . cmd
pt . x = p [ i ] . x
pt . y = p [ i ] . y
pt . ctrl1 = Point ( p [ i ] . ctrl1 . x , p [ i ] . ctrl1 . y )
pt . ctrl2 = Point ( p [ i ] . ctrl2 . x , p [ i ] . ctrl2 . y )
pt . freehand = False
self . _points . insert ( i , pt )
self . _points [ i - 1 ] . ctrl1 = Point ( p [ i - 1 ] . ctrl1 . x , p [ i - 1 ] . ctrl1 . y )
self . _points [ i + 1 ] . ctrl1 = Point ( p [ i + 1 ] . ctrl1 . x , p [ i + 1 ] . ctrl1 . y )
self . _points [ i + 1 ] . ctrl2 = Point ( p [ i + 1 ] . ctrl2 . x , p [ i + 1 ] . ctrl2 . y )


signature:
def update ( self ) :
    """Update runs each frame to check for mouse interaction .
 Alters the path by allowing the user to add new points ,
 drag point handles and move their location .
 Updates are automatically stored as SVG
 in the given filename ."""

implementation:
x , y = mouse ( )
if self . show_grid :
    x , y = self . grid . snap ( x , y )
if _ctx . _ns [ "mousedown" ] and not self . freehand :
    self . _dirty = True
    # Handle buttons first .
    # When pressing down on a button , all other action halts .
    # Buttons appear near a point being edited .
    # Once clicked , actions are resolved .
    if self . edit != None and not self . drag_point and not self . drag_handle1 and not self . drag_handle2 :
        pt = self . _points [ self . edit ]
        dx = pt . x + self . btn_x
        dy = pt . y + self . btn_y
        # The delete button
        if self . overlap ( dx , dy , x , y , r = self . btn_r ) :
            self . delete = self . edit
            return
        # The moveto button ,
        # active on the last point in the path .
        dx += self . btn_r * 2 + 2
        if self . edit == len ( self . _points ) - 1 and self . overlap ( dx , dy , x , y , r = self . btn_r ) :
            self . moveto = self . edit
            return
    if self . insert :
        self . inserting = True
        return
    # When not dragging a point or the handle of a point ,
    # i . e . the mousebutton was released and then pressed again ,
    # check to see if a point on the path is pressed .
    # When this point is not the last new point ,
    # enter edit mode .
    if not self . drag_point and not self . drag_handle1 and not self . drag_handle2 :
        self . editing = False
        indices = range ( len ( self . _points ) )
        indices . reverse ( )
        for i in indices :
            pt = self . _points [ i ]
            if pt != self . new and self . overlap ( x , y , pt . x , pt . y ) and self . new == None : # Don ' t select a point if in fact
            # it is at the same location of the first handle
            # of the point we are currently editing .
                if self . edit == i + 1 and self . overlap ( self . _points [ i + 1 ] . ctrl1 . x , self . _points [ i + 1 ] . ctrl1 . y , x , y ) :
                    continue
                else :
                    self . edit = i
                    self . editing = True
                    break
    # When the mouse button is down ,
    # edit mode continues as long as
    # a point or handle is dragged .
    # Else , stop editing and switch to add - mode
    # ( the user is clicking somewhere on the canvas ) .
    if not self . editing :
        if self . edit != None :
            pt = self . _points [ self . edit ]
            if self . overlap ( pt . ctrl1 . x , pt . ctrl1 . y , x , y ) or self . overlap ( pt . ctrl2 . x , pt . ctrl2 . y , x , y ) :
                self . editing = True
            else :
                self . edit = None
    # When not in edit mode , there are two options .
    # Either no new point is defined and the user is
    # clicking somewhere on the canvas ( add a new point )
    # or the user is dragging the handle of the new point .
    # Adding a new point is a fluid click - to - locate and
    # drag - to - curve action .
    if self . edit == None :
        if self . new == None : # A special case is when the used clicked
        # the moveto button on the last point in the path .
        # This indicates a gap ( i . e . MOVETO ) in the path .
            self . new = PathElement ( )
            if self . moveto == True or len ( self . _points ) == 0 :
                cmd = MOVETO
                self . moveto = None
                self . last_moveto = self . new
            else :
                cmd = CURVETO
            self . new . cmd = cmd
            self . new . x = x
            self . new . y = y
            self . new . ctrl1 = Point ( x , y )
            self . new . ctrl2 = Point ( x , y )
            self . new . freehand = False
            # Don ' t forget to map the point ' s ctrl1 handle
            # to the ctrl2 handle of the previous point .
            # This makes for smooth , continuous paths .
            if len ( self . _points ) > 0 :
                prev = self . _points [ - 1 ]
                rx , ry = self . reflect ( prev . x , prev . y , prev . ctrl2 . x , prev . ctrl2 . y )
                self . new . ctrl1 = Point ( rx , ry )
            self . _points . append ( self . new )
        else : # Illustrator - like behavior :
        # when the handle is dragged downwards ,
        # the path bulges upwards .
            rx , ry = self . reflect ( self . new . x , self . new . y , x , y )
            self . new . ctrl2 = Point ( rx , ry )
    # Edit mode
    elif self . new == None :
        pt = self . _points [ self . edit ]
        # The user is pressing the mouse on a point ,
        # enter drag - point mode .
        if self . overlap ( pt . x , pt . y , x , y ) and not self . drag_handle1 and not self . drag_handle2 and not self . new != None :
            self . drag_point = True
            self . drag_handle1 = False
            self . drag_handle2 = False
        # The user is pressing the mouse on a point ' s handle ,
        # enter drag - handle mode .
        if self . overlap ( pt . ctrl1 . x , pt . ctrl1 . y , x , y ) and pt . cmd == CURVETO and not self . drag_point and not self . drag_handle2 :
            self . drag_point = False
            self . drag_handle1 = True
            self . drag_handle2 = False
        if self . overlap ( pt . ctrl2 . x , pt . ctrl2 . y , x , y ) and pt . cmd == CURVETO and not self . drag_point and not self . drag_handle1 :
            self . drag_point = False
            self . drag_handle1 = False
            self . drag_handle2 = True
        # In drag - point mode ,
        # the point is located at the mouse coordinates .
        # The handles move relatively to the new location
        # ( e . g . they are retained , the path does not distort ) .
        # Modify the ctrl1 handle of the next point as well .
        if self . drag_point == True :
            dx = x - pt . x
            dy = y - pt . y
            pt . x = x
            pt . y = y
            pt . ctrl2 . x += dx
            pt . ctrl2 . y += dy
            if self . edit < len ( self . _points ) - 1 :
                rx , ry = self . reflect ( pt . x , pt . y , x , y )
                next = self . _points [ self . edit + 1 ]
                next . ctrl1 . x += dx
                next . ctrl1 . y += dy
        # In drag - handle mode ,
        # set the path ' s handle to the mouse location .
        # Rotate the handle of the next or previous point
        # to keep paths smooth - unless the user is pressing " x " .
        if self . drag_handle1 == True :
            pt . ctrl1 = Point ( x , y )
            if self . edit > 0 and self . last_key != "x" :
                prev = self . _points [ self . edit - 1 ]
                d = self . distance ( prev . x , prev . y , prev . ctrl2 . x , prev . ctrl2 . y )
                a = self . angle ( prev . x , prev . y , pt . ctrl1 . x , pt . ctrl1 . y )
                prev . ctrl2 = self . coordinates ( prev . x , prev . y , d , a + 180 )
        if self . drag_handle2 == True :
            pt . ctrl2 = Point ( x , y )
            if self . edit < len ( self . _points ) - 1 and self . last_key != "x" :
                next = self . _points [ self . edit + 1 ]
                d = self . distance ( pt . x , pt . y , next . ctrl1 . x , next . ctrl1 . y )
                a = self . angle ( pt . x , pt . y , pt . ctrl2 . x , pt . ctrl2 . y )
                next . ctrl1 = self . coordinates ( pt . x , pt . y , d , a + 180 )
elif not self . freehand : # The mouse button is released
# so we are not dragging anything around .
    self . new = None
    self . drag_point = False
    self . drag_handle1 = False
    self . drag_handle2 = False
    # The delete button for a point was clicked .
    if self . delete != None and len ( self . _points ) > 0 :
        i = self . delete
        cmd = self . _points [ i ] . cmd
        del self . _points [ i ]
        if 0 < i < len ( self . _points ) :
            prev = self . _points [ i - 1 ]
            rx , ry = self . reflect ( prev . x , prev . y , prev . ctrl2 . x , prev . ctrl2 . y )
            self . _points [ i ] . ctrl1 = Point ( rx , ry )
        # Also delete all the freehand points
        # prior to this point .
        start_i = i
        while i > 1 :
            i -= 1
            pt = self . _points [ i ]
            if pt . freehand :
                del self . _points [ i ]
            elif i < start_i - 1 and pt . freehand == False :
                if pt . cmd == MOVETO :
                    del self . _points [ i ]
                break
        # When you delete a MOVETO point ,
        # the last moveto ( the one where the dashed line points to )
        # needs to be updated .
        if len ( self . _points ) > 0 and ( cmd == MOVETO or i == 0 ) :
            self . last_moveto = self . _points [ 0 ]
            for pt in self . _points :
                if pt . cmd == MOVETO :
                    self . last_moveto = pt
        self . delete = None
        self . edit = None
    # The moveto button for the last point
    # in the path was clicked .
    elif isinstance ( self . moveto , int ) :
        self . moveto = True
        self . edit = None
    # We are not editing a node and
    # the mouse is hovering over the path outline stroke :
    # it is possible to insert a point here .
    elif self . edit == None and self . contains_point ( x , y , d = 2 ) :
        self . insert = True
    else :
        self . insert = False
    # Commit insert of new point .
    if self . inserting and self . contains_point ( x , y , d = 2 ) :
        self . insert_point ( x , y )
        self . insert = False
    self . inserting = False
    # No modifications are being made right now
    # and the SVG file needs to be updated .
    if self . _dirty == True :
        self . export_svg ( )
        self . _dirty = False
# Keyboard interaction .
if _ctx . _ns [ "keydown" ] :
    self . last_key = _ctx . _ns [ "key" ]
    self . last_keycode = _ctx . _ns [ "keycode" ]
if not _ctx . _ns [ "keydown" ] and self . last_key != None : # If the TAB - key is pressed ,
# switch the magnetic grid either on or off .
    if self . last_keycode == KEY_TAB :
        self . show_grid = not self . show_grid
    # When " f " is pressed , switch freehand mode .
    if self . last_key == "f" :
        self . edit = None
        self . freehand = not self . freehand
        if self . freehand :
            self . msg = "freehand"
        else :
            self . msg = "curves"
    # When ESC is pressed exit edit mode .
    if self . last_keycode == KEY_ESC :
        self . edit = None
    # When BACKSPACE is pressed , delete current point .
    if self . last_keycode == _ctx . KEY_BACKSPACE and self . edit != None :
        self . delete = self . edit
    self . last_key = None
    self . last_code = None
# Using the keypad you can scroll the screen .
if _ctx . _ns [ "keydown" ] :
    dx = 0
    dy = 0
    keycode = _ctx . _ns [ "keycode" ]
    if keycode == _ctx . KEY_LEFT :
        dx = - 10
    elif keycode == _ctx . KEY_RIGHT :
        dx = 10
    if keycode == _ctx . KEY_UP :
        dy = - 10
    elif keycode == _ctx . KEY_DOWN :
        dy = 10
    if dx != 0 or dy != 0 :
        for pt in self . _points :
            pt . x += dx
            pt . y += dy
            pt . ctrl1 . x += dx
            pt . ctrl1 . y += dy
            pt . ctrl2 . x += dx
            pt . ctrl2 . y += dy


signature:
def draw ( self ) :
    """Draws the editable path and interface elements ."""

implementation:
# Enable interaction .
self . update ( )
x , y = mouse ( )
# Snap to grid when enabled .
# The grid is enabled with the TAB key .
if self . show_grid :
    self . grid . draw ( )
    x , y = self . grid . snap ( x , y )
_ctx . strokewidth ( self . strokewidth )
if self . freehand :
    self . draw_freehand ( )
r = 4
_ctx . nofill ( )
if len ( self . _points ) > 0 :
    first = True
    for i in range ( len ( self . _points ) ) : # Construct the path .
        pt = self . _points [ i ]
        if first :
            _ctx . beginpath ( pt . x , pt . y )
            first = False
        else :
            if pt . cmd == CLOSE :
                _ctx . closepath ( )
            elif pt . cmd == MOVETO :
                _ctx . moveto ( pt . x , pt . y )
            elif pt . cmd == LINETO :
                _ctx . lineto ( pt . x , pt . y )
            elif pt . cmd == CURVETO :
                _ctx . curveto ( pt . ctrl1 . x , pt . ctrl1 . y , pt . ctrl2 . x , pt . ctrl2 . y , pt . x , pt . y )
        # In add - or edit - mode ,
        # display the current point ' s handles .
        if ( ( i == self . edit and self . new == None ) or pt == self . new ) and pt . cmd == CURVETO and not pt . freehand :
            _ctx . stroke ( self . handle_color )
            _ctx . nofill ( )
            _ctx . oval ( pt . x - r , pt . y - r , r * 2 , r * 2 )
            _ctx . stroke ( self . handle_color )
            _ctx . line ( pt . ctrl2 . x , pt . ctrl2 . y , pt . x , pt . y )
            _ctx . fill ( self . handle_color )
        # Display the new point ' s handle being dragged .
        if pt == self . new and not pt . freehand :
            rx , ry = self . reflect ( pt . x , pt . y , pt . ctrl2 . x , pt . ctrl2 . y )
            _ctx . stroke ( self . handle_color )
            _ctx . line ( rx , ry , pt . x , pt . y )
            _ctx . nostroke ( )
            _ctx . fill ( self . handle_color )
            _ctx . oval ( rx - r / 2 , ry - r / 2 , r , r )
        # Display handles for point being edited .
        if i == self . edit and self . new == None and pt . cmd == CURVETO and not pt . freehand :
            _ctx . oval ( pt . ctrl2 . x - r / 2 , pt . ctrl2 . y - r / 2 , r , r )
            if i > 0 :
                prev = self . _points [ i - 1 ]
                _ctx . line ( pt . ctrl1 . x , pt . ctrl1 . y , prev . x , prev . y )
                _ctx . oval ( pt . ctrl1 . x - r / 2 , pt . ctrl1 . y - r / 2 , r , r )
            if i > 0 and self . _points [ i - 1 ] . cmd != MOVETO :
                _ctx . line ( prev . ctrl2 . x , prev . ctrl2 . y , prev . x , prev . y )
            if i < len ( self . _points ) - 1 :
                next = self . _points [ i + 1 ]
                if next . cmd == CURVETO :
                    _ctx . line ( next . ctrl1 . x , next . ctrl1 . y , pt . x , pt . y )
        # When hovering over a point ,
        # highlight it .
        elif self . overlap ( x , y , pt . x , pt . y ) and not pt . freehand :
            self . insert = False
            # quit insert mode
            _ctx . nofill ( )
            _ctx . stroke ( self . handle_color )
            _ctx . oval ( pt . x - r , pt . y - r , r * 2 , r * 2 )
        # Provide visual coordinates
        # for points being dragged , moved or hovered .
        _ctx . fontsize ( 9 )
        _ctx . fill ( self . handle_color )
        txt = " (" + str ( int ( pt . x ) ) + ", " + str ( int ( pt . y ) ) + ")"
        if ( i == self . edit and self . new == None ) or pt == self . new and not pt . freehand :
            _ctx . text ( txt , pt . x + r , pt . y + 2 )
        elif self . overlap ( x , y , pt . x , pt . y ) and not pt . freehand :
            _ctx . text ( txt , pt . x + r , pt . y + 2 )
        # Draw a circle for each point
        # in the path .
        if not pt . freehand :
            if pt . cmd != MOVETO :
                _ctx . fill ( self . path_color )
                _ctx . nostroke ( )
            else :
                _ctx . stroke ( self . path_color )
                _ctx . nofill ( )
            _ctx . oval ( pt . x - r / 2 , pt . y - r / 2 , r , r )
    # Draw the current path ,
    # update the path property .
    _ctx . stroke ( self . path_color )
    _ctx . fill ( self . path_fill )
    _ctx . autoclosepath ( False )
    p = _ctx . endpath ( )
    self . path = p
    # Possible to insert a point here .
    if self . insert :
        _ctx . stroke ( self . handle_color )
        _ctx . nofill ( )
        _ctx . oval ( x - r * 0.8 , y - r * 0.8 , r * 1.6 , r * 1.6 )
    # When not editing a node ,
    # prospect how the curve will continue
    # when adding a new point .
    if self . edit == None and self . new == None and self . moveto != True and not self . freehand :
        _ctx . nofill ( )
        _ctx . stroke ( self . new_color )
        rx , ry = self . reflect ( pt . x , pt . y , pt . ctrl2 . x , pt . ctrl2 . y )
        _ctx . beginpath ( pt . x , pt . y )
        _ctx . curveto ( rx , ry , x , y , x , y )
        _ctx . endpath ( )
        # A dashed line indicates what
        # a CLOSETO would look like .
        if self . last_moveto != None :
            start = self . last_moveto
        else :
            start = self . _points [ 0 ]
        p = _ctx . line ( x , y , start . x , start . y , draw = False )
        try :
            p . _nsBezierPath . setLineDash_count_phase_ ( [ 2 , 4 ] , 2 , 50 )
        except :
            pass
        _ctx . drawpath ( p )
    # When doing a MOVETO ,
    # show the new point hovering at the mouse location .
    elif self . edit == None and self . new == None and self . moveto != None :
        _ctx . stroke ( self . new_color )
        _ctx . nofill ( )
        _ctx . oval ( x - r * 0.8 , y - r * 0.8 , r * 1.6 , r * 1.6 )
    # Draws button for a point being edited .
    # The first button deletes the point .
    # The second button , which appears only on the last point
    # in the path , tells the editor to perform a MOVETO
    # before adding a new point .
    if self . edit != None :
        pt = self . _points [ self . edit ]
        x = pt . x + self . btn_x
        y = pt . y + self . btn_y
        r = self . btn_r
        _ctx . nostroke ( )
        _ctx . fill ( 0 , 0 , 0 , 0.2 )
        _ctx . fill ( self . handle_color )
        _ctx . oval ( x - r , y - r , r * 2 , r * 2 )
        _ctx . fill ( 1 )
        _ctx . rotate ( 45 )
        _ctx . rect ( x - r + 2 , y - 0.625 , r + 1 , 1.25 )
        _ctx . rotate ( - 90 )
        _ctx . rect ( x - r + 2 , y - 0.625 , r + 1 , 1.25 )
        _ctx . reset ( )
        if self . edit == len ( self . _points ) - 1 :
            _ctx . fill ( self . handle_color )
            _ctx . oval ( x + r * 2 + 2 - r , y - r , r * 2 , r * 2 )
            _ctx . fill ( 1 )
            _ctx . rect ( x + r * 2 + 2 - 2.25 , y - r + 3 , 1.5 , r - 1 )
            _ctx . rect ( x + r * 2 + 2 + 0.75 , y - r + 3 , 1.5 , r - 1 )
# Handle onscreen notifications .
# Any text in msg is displayed in a box in the center
# and slowly fades away , after which msg is cleared .
if self . msg != "" :
    self . msg_alpha -= 0.1
    _ctx . nostroke ( )
    _ctx . fill ( 0 , 0 , 0 , self . msg_alpha )
    _ctx . fontsize ( 18 )
    _ctx . lineheight ( 1 )
    w = _ctx . textwidth ( self . msg )
    _ctx . rect ( _ctx . WIDTH / 2 - w / 2 - 9 , _ctx . HEIGHT / 2 - 27 , w + 18 , 36 , roundness = 0.4 )
    _ctx . fill ( 1 , 1 , 1 , 0.8 )
    _ctx . align ( CENTER )
    _ctx . text ( self . msg , 0 , _ctx . HEIGHT / 2 , width = _ctx . WIDTH )
if self . msg_alpha <= 0.0 :
    self . msg = ""
    self . msg_alpha = 1.0


signature:
def draw_freehand ( self ) :
    """Freehand sketching ."""

implementation:
if _ctx . _ns [ "mousedown" ] :
    x , y = mouse ( )
    if self . show_grid :
        x , y = self . grid . snap ( x , y )
    if self . freehand_move == True :
        cmd = MOVETO
        self . freehand_move = False
    else :
        cmd = LINETO
    # Add a new LINETO to the path ,
    # except when starting to draw ,
    # then a MOVETO is added to the path .
    pt = PathElement ( )
    if cmd != MOVETO :
        pt . freehand = True
        # Used when mixed with curve drawing .
    else :
        pt . freehand = False
    pt . cmd = cmd
    pt . x = x
    pt . y = y
    pt . ctrl1 = Point ( x , y )
    pt . ctrl2 = Point ( x , y )
    self . _points . append ( pt )
    # Draw the current location of the cursor .
    r = 4
    _ctx . nofill ( )
    _ctx . stroke ( self . handle_color )
    _ctx . oval ( pt . x - r , pt . y - r , r * 2 , r * 2 )
    _ctx . fontsize ( 9 )
    _ctx . fill ( self . handle_color )
    _ctx . text ( " (" + str ( int ( pt . x ) ) + ", " + str ( int ( pt . y ) ) + ")" , pt . x + r , pt . y )
    self . _dirty = True
else : # Export the updated drawing ,
# remember to do a MOVETO on the next interaction .
    self . freehand_move = True
    if self . _dirty :
        self . _points [ - 1 ] . freehand = False
        self . export_svg ( )
        self . _dirty = False


signature:
def export_svg ( self ) :
    """Exports the path as SVG .
 Uses the filename given when creating this object .
 The file is automatically updated to reflect
 changes to the path ."""

implementation:
d = ""
if len ( self . _points ) > 0 :
    d += "M " + str ( self . _points [ 0 ] . x ) + " " + str ( self . _points [ 0 ] . y ) + " "
    for pt in self . _points :
        if pt . cmd == MOVETO :
            d += "M " + str ( pt . x ) + " " + str ( pt . y ) + " "
        elif pt . cmd == LINETO :
            d += "L " + str ( pt . x ) + " " + str ( pt . y ) + " "
        elif pt . cmd == CURVETO :
            d += "C "
            d += str ( pt . ctrl1 . x ) + " " + str ( pt . ctrl1 . y ) + " "
            d += str ( pt . ctrl2 . x ) + " " + str ( pt . ctrl2 . y ) + " "
            d += str ( pt . x ) + " " + str ( pt . y ) + " "
c = "rgb("
c += str ( int ( self . path_color . r * 255 ) ) + ","
c += str ( int ( self . path_color . g * 255 ) ) + ","
c += str ( int ( self . path_color . b * 255 ) ) + ")"
s = '<?xml version="1.0"?>\n'
s += '<svg width="' + str ( _ctx . WIDTH ) + 'pt" height="' + str ( _ctx . HEIGHT ) + 'pt">\n'
s += '<g>\n'
s += '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="' + str ( self . strokewidth ) + '" />\n'
s += '</g>\n'
s += '</svg>\n'
f = open ( self . file + ".svg" , "w" )
f . write ( s )
f . close ( )


signature:
def download ( self , size = SIZE_XLARGE , thumbnail = False , wait = 60 , asynchronous = False ) :
    """Downloads this image to cache .
 Calling the download ( ) method instantiates an asynchronous URLAccumulator
 that will fetch the image ' s URL from Flickr .
 A second process then downloads the file at the retrieved URL .
 Once it is done downloading , this image will have its path property
 set to an image file in the cache ."""

implementation:
if thumbnail == True :
    size = SIZE_THUMBNAIL
    # backwards compatibility
self . _size = disambiguate_size ( size )
self . _wait = wait
self . _asynchronous = asynchronous
url = "http://api.flickr.com/services/rest/?method=flickr.photos.getSizes"
url += "&photo_id=" + self . id
url += "&api_key=" + API_KEY
URLAccumulator . __init__ ( self , url , wait , asynchronous , "flickr" , ".xml" , 2 )
if not asynchronous :
    return self . path


signature:
def gtk_mouse_button_down ( self , widget , event ) :
    '''Handle right mouse button clicks'''

implementation:
if self . menu_enabled and event . button == 3 :
    menu = self . uimanager . get_widget ( '/Save as' )
    menu . popup ( None , None , None , None , event . button , event . time )
else :
    super ( ShoebotWindow , self ) . gtk_mouse_button_down ( widget , event )


signature:
def show_variables_window ( self ) :
    """Show the variables window ."""

implementation:
if self . var_window is None and self . bot . _vars :
    self . var_window = VarWindow ( self , self . bot , '%s variables' % ( self . title or 'Shoebot' ) )
    self . var_window . window . connect ( "destroy" , self . var_window_closed )


signature:
def hide_variables_window ( self ) :
    """Hide the variables window"""

implementation:
if self . var_window is not None :
    self . var_window . window . destroy ( )
    self . var_window = None


signature:
def var_window_closed ( self , widget ) :
    """Called if user clicked close button on var window
 : param widget :
 : return :"""

implementation:
# TODO - Clean up the menu handling stuff its a bit spagetti right now
self . action_group . get_action ( 'vars' ) . set_active ( False )
self . show_vars = False
self . var_window = None


signature:
def schedule_snapshot ( self , format ) :
    """Tell the canvas to perform a snapshot when it ' s finished rendering
 : param format :
 : return :"""

implementation:
bot = self . bot
canvas = self . bot . canvas
script = bot . _namespace [ '__file__' ]
if script :
    filename = os . path . splitext ( script ) [ 0 ] + '.' + format
else :
    filename = 'output.' + format
f = canvas . output_closure ( filename , self . bot . _frame )
self . scheduled_snapshots . append ( f )


signature:
def trigger_fullscreen_action ( self , fullscreen ) :
    """Toggle fullscreen from outside the GUI ,
 causes the GUI to updated and run all its actions ."""

implementation:
action = self . action_group . get_action ( 'fullscreen' )
action . set_active ( fullscreen )


signature:
def do_fullscreen ( self , widget ) :
    """Widget Action to Make the window fullscreen and update the bot ."""

implementation:
self . fullscreen ( )
self . is_fullscreen = True
# next lines seem to be needed for window switching really to
# fullscreen mode before reading it ' s size values
while Gtk . events_pending ( ) :
    Gtk . main_iteration ( )
# we pass informations on full - screen size to bot
self . bot . _screen_width = Gdk . Screen . width ( )
self . bot . _screen_height = Gdk . Screen . height ( )
self . bot . _screen_ratio = self . bot . _screen_width / self . bot . _screen_height


signature:
def do_unfullscreen ( self , widget ) :
    """Widget Action to set Windowed Mode ."""

implementation:
self . unfullscreen ( )
self . is_fullscreen = False
self . bot . _screen_ratio = None


signature:
def do_window_close ( self , widget , data = None ) :
    """Widget Action to Close the window , triggering the quit event ."""

implementation:
publish_event ( QUIT_EVENT )
if self . has_server :
    self . sock . close ( )
self . hide_variables_window ( )
self . destroy ( )
self . window_open = False


signature:
def do_toggle_fullscreen ( self , action ) :
    """Widget Action to Toggle fullscreen from the GUI"""

implementation:
is_fullscreen = action . get_active ( )
if is_fullscreen :
    self . fullscreen ( )
else :
    self . unfullscreen ( )


signature:
def do_toggle_play ( self , action ) :
    """Widget Action to toggle play / pause ."""

implementation:
# TODO - move this into bot controller
# along with stuff in socketserver and shell
if self . pause_speed is None and not action . get_active ( ) :
    self . pause_speed = self . bot . _speed
    self . bot . _speed = 0
else :
    self . bot . _speed = self . pause_speed
    self . pause_speed = None


signature:
def do_toggle_variables ( self , action ) :
    """Widget Action to toggle showing the variables window ."""

implementation:
self . show_vars = action . get_active ( )
if self . show_vars :
    self . show_variables_window ( )
else :
    self . hide_variables_window ( )


signature:
def main_iteration ( self ) :
    """Called from main loop , if your sink needs to handle GUI events
 do it here .
 Check any GUI flags then call Gtk . main _ iteration to update things ."""

implementation:
if self . show_vars :
    self . show_variables_window ( )
else :
    self . hide_variables_window ( )
for snapshot_f in self . scheduled_snapshots :
    fn = snapshot_f ( self . last_draw_ctx )
    print ( "Saved snapshot: %s" % fn )
else :
    self . scheduled_snapshots = deque ( )
while Gtk . events_pending ( ) :
    Gtk . main_iteration ( )


signature:
def _set_initial_defaults ( self ) :
    '''Set the default values . Called at _ _ init _ _ and at the end of run ( ) ,
 do that new draw loop iterations don ' t take up values left over by the
 previous one .'''

implementation:
DEFAULT_WIDTH , DEFAULT_HEIGHT = self . _canvas . DEFAULT_SIZE
self . WIDTH = self . _namespace . get ( 'WIDTH' , DEFAULT_WIDTH )
self . HEIGHT = self . _namespace . get ( 'HEIGHT' , DEFAULT_WIDTH )
if 'WIDTH' in self . _namespace or 'HEIGHT' in self . _namespace :
    self . size ( w = self . _namespace . get ( 'WIDTH' ) , h = self . _namespace . get ( 'HEIGHT' ) )
self . _transformmode = Bot . CENTER
self . _canvas . settings ( fontfile = "assets/notcouriersans.ttf" , fontsize = 16 , align = Bot . LEFT , lineheight = 1 , fillcolor = self . color ( .2 ) , strokecolor = None , strokewidth = 1.0 , background = self . color ( 1 , 1 , 1 ) )


signature:
def _mouse_pointer_moved ( self , x , y ) :
    '''GUI callback for mouse moved'''

implementation:
self . _namespace [ 'MOUSEX' ] = x
self . _namespace [ 'MOUSEY' ] = y


signature:
def _key_pressed ( self , key , keycode ) :
    '''GUI callback for key pressed'''

implementation:
self . _namespace [ 'key' ] = key
self . _namespace [ 'keycode' ] = keycode
self . _namespace [ 'keydown' ] = True


signature:
def _makeInstance ( self , clazz , args , kwargs ) :
    '''Creates an instance of a class defined in this document .
 This method sets the context of the object to the current context .'''

implementation:
inst = clazz ( self , * args , ** kwargs )
return inst


signature:
def _makeColorableInstance ( self , clazz , args , kwargs ) :
    """Create an object , if fill , stroke or strokewidth
 is not specified , get them from the _ canvas
 : param clazz :
 : param args :
 : param kwargs :
 : return :"""

implementation:
kwargs = dict ( kwargs )
fill = kwargs . get ( 'fill' , self . _canvas . fillcolor )
if not isinstance ( fill , Color ) :
    fill = Color ( fill , mode = 'rgb' , color_range = 1 )
kwargs [ 'fill' ] = fill
stroke = kwargs . get ( 'stroke' , self . _canvas . strokecolor )
if not isinstance ( stroke , Color ) :
    stroke = Color ( stroke , mode = 'rgb' , color_range = 1 )
kwargs [ 'stroke' ] = stroke
kwargs [ 'strokewidth' ] = kwargs . get ( 'strokewidth' , self . _canvas . strokewidth )
inst = clazz ( self , * args , ** kwargs )
return inst


signature:
def color ( self , * args ) :
    ''': param args : color in a supported format .
 : return : Color object containing the color .'''

implementation: return self . Color ( mode = self . color_mode , color_range = self . color_range , * args )


signature:
def grid ( self , cols , rows , colSize = 1 , rowSize = 1 , shuffled = False ) :
    """Returns an iterator that contains coordinate tuples .
 The grid can be used to quickly create grid - like structures .
 A common way to use them is :
 for x , y in grid ( 10,10,12,12 ) :
 rect ( x , y , 10,10)"""

implementation:
# Taken ipsis verbis from Nodebox
from random import shuffle
rowRange = range ( int ( rows ) )
colRange = range ( int ( cols ) )
if ( shuffled ) :
    shuffle ( rowRange )
    shuffle ( colRange )
for y in rowRange :
    for x in colRange :
        yield ( x * colSize , y * rowSize )


signature:
def snapshot ( self , target = None , defer = None , autonumber = False ) :
    '''Save the contents of current surface into a file or cairo surface / context
 : param filename : Can be a filename or a Cairo surface .
 : param defer : If true , buffering / threading may be employed however output will not be immediate .
 : param autonumber : If true then a number will be appended to the filename .'''

implementation:
if autonumber :
    file_number = self . _frame
else :
    file_number = None
if isinstance ( target , cairo . Surface ) : # snapshot to Cairo surface
    if defer is None :
        self . _canvas . snapshot ( surface , defer )
        defer = False
    ctx = cairo . Context ( target )
    # this used to be self . _ canvas . snapshot , but I couldn ' t make it work .
    # self . _ canvas . snapshot ( target , defer )
    # TODO : check if this breaks when taking more than 1 snapshot
    self . _canvas . _drawqueue . render ( ctx )
    return
elif target is None : # If nothing specified , use a default filename from the script name
    script_file = self . _namespace . get ( '__file__' )
    if script_file :
        target = os . path . splitext ( script_file ) [ 0 ] + '.svg'
        file_number = True
if target : # snapshot to file , target is a filename
    if defer is None :
        defer = True
    self . _canvas . snapshot ( target , defer = defer , file_number = file_number )
else :
    raise ShoebotError ( 'No image saved' )


signature:
def show ( self , format = 'png' , as_data = False ) :
    '''Returns an Image object of the current surface . Used for displaying
 output in Jupyter notebooks . Adapted from the cairo - jupyter project .'''

implementation:
from io import BytesIO
b = BytesIO ( )
if format == 'png' :
    from IPython . display import Image
    surface = cairo . ImageSurface ( cairo . FORMAT_ARGB32 , self . WIDTH , self . HEIGHT )
    self . snapshot ( surface )
    surface . write_to_png ( b )
    b . seek ( 0 )
    data = b . read ( )
    if as_data :
        return data
    else :
        return Image ( data )
elif format == 'svg' :
    from IPython . display import SVG
    surface = cairo . SVGSurface ( b , self . WIDTH , self . HEIGHT )
    surface . finish ( )
    b . seek ( 0 )
    data = b . read ( )
    if as_data :
        return data
    else :
        return SVG ( data )


signature:
def ximport ( self , libName ) :
    '''Import Nodebox libraries .
 The libraries get _ ctx , which provides
 them with the nodebox API .
 : param libName : Library name to import'''

implementation:
# from Nodebox
lib = __import__ ( libName )
self . _namespace [ libName ] = lib
lib . _ctx = self
return lib


signature:
def size ( self , w = None , h = None ) :
    '''Set the canvas size
 Only the first call will actually be effective .
 : param w : Width
 : param h : height'''

implementation:
if not w :
    w = self . _canvas . width
if not h :
    h = self . _canvas . height
if not w and not h :
    return ( self . _canvas . width , self . _canvas . height )
# FIXME : Updating in all these places seems a bit hacky
w , h = self . _canvas . set_size ( ( w , h ) )
self . _namespace [ 'WIDTH' ] = w
self . _namespace [ 'HEIGHT' ] = h
self . WIDTH = w
# Added to make evolution example work
self . HEIGHT = h


signature:
def speed ( self , framerate = None ) :
    '''Set animation framerate .
 : param framerate : Frames per second to run bot .
 : return : Current framerate of animation .'''

implementation:
if framerate is not None :
    self . _speed = framerate
    self . _dynamic = True
else :
    return self . _speed


signature:
def set_callbacks ( self , ** kwargs ) :
    '''Set callbacks for input events'''

implementation:
for name in self . SUPPORTED_CALLBACKS :
    func = kwargs . get ( name , getattr ( self , name ) )
    setattr ( self , name , func )


signature:
def complement ( clr ) :
    """Returns the color and its complement in a list ."""

implementation:
clr = color ( clr )
colors = colorlist ( clr )
colors . append ( clr . complement )
return colors


signature:
def complementary ( clr ) :
    """Returns a list of complementary colors .
 The complement is the color 180 degrees across
 the artistic RYB color wheel .
 The list contains darker and softer contrasting
 and complementing colors ."""

implementation:
clr = color ( clr )
colors = colorlist ( clr )
# A contrasting color : much darker or lighter than the original .
c = clr . copy ( )
if clr . brightness > 0.4 :
    c . brightness = 0.1 + c . brightness * 0.25
else :
    c . brightness = 1.0 - c . brightness * 0.25
colors . append ( c )
# A soft supporting color : lighter and less saturated .
c = clr . copy ( )
c . brightness = 0.3 + c . brightness
c . saturation = 0.1 + c . saturation * 0.3
colors . append ( c )
# A contrasting complement : very dark or very light .
clr = clr . complement
c = clr . copy ( )
if clr . brightness > 0.3 :
    c . brightness = 0.1 + clr . brightness * 0.25
else :
    c . brightness = 1.0 - c . brightness * 0.25
colors . append ( c )
# The complement and a light supporting variant .
colors . append ( clr )
c = clr . copy ( )
c . brightness = 0.3 + c . brightness
c . saturation = 0.1 + c . saturation * 0.25
colors . append ( c )
return colors


signature:
def split_complementary ( clr ) :
    """Returns a list with the split complement of the color .
 The split complement are the two colors to the left and right
 of the color ' s complement ."""

implementation:
clr = color ( clr )
colors = colorlist ( clr )
clr = clr . complement
colors . append ( clr . rotate_ryb ( - 30 ) . lighten ( 0.1 ) )
colors . append ( clr . rotate_ryb ( 30 ) . lighten ( 0.1 ) )
return colors


signature:
def left_complement ( clr ) :
    """Returns the left half of the split complement .
 A list is returned with the same darker and softer colors
 as in the complementary list , but using the hue of the
 left split complement instead of the complement itself ."""

implementation:
left = split_complementary ( clr ) [ 1 ]
colors = complementary ( clr )
colors [ 3 ] . h = left . h
colors [ 4 ] . h = left . h
colors [ 5 ] . h = left . h
colors = colorlist ( colors [ 0 ] , colors [ 2 ] , colors [ 1 ] , colors [ 3 ] , colors [ 4 ] , colors [ 5 ] )
return colors


signature:
def right_complement ( clr ) :
    """Returns the right half of the split complement ."""

implementation:
right = split_complementary ( clr ) [ 2 ]
colors = complementary ( clr )
colors [ 3 ] . h = right . h
colors [ 4 ] . h = right . h
colors [ 5 ] . h = right . h
colors = colorlist ( colors [ 0 ] , colors [ 2 ] , colors [ 1 ] , colors [ 5 ] , colors [ 4 ] , colors [ 3 ] )
return colors


signature:
def analogous ( clr , angle = 10 , contrast = 0.25 ) :
    """Returns colors that are next to each other on the wheel .
 These yield natural color schemes ( like shades of water or sky ) .
 The angle determines how far the colors are apart ,
 making it bigger will introduce more variation .
 The contrast determines the darkness / lightness of
 the analogue colors in respect to the given colors ."""

implementation:
contrast = max ( 0 , min ( contrast , 1.0 ) )
clr = color ( clr )
colors = colorlist ( clr )
for i , j in [ ( 1 , 2.2 ) , ( 2 , 1 ) , ( - 1 , - 0.5 ) , ( - 2 , 1 ) ] :
    c = clr . rotate_ryb ( angle * i )
    t = 0.44 - j * 0.1
    if clr . brightness - contrast * j < t :
        c . brightness = t
    else :
        c . brightness = clr . brightness - contrast * j
    c . saturation -= 0.05
    colors . append ( c )
return colors


signature:
def monochrome ( clr ) :
    """Returns colors in the same hue with varying brightness / saturation ."""

implementation:
def _wrap ( x , min , threshold , plus ) :
    if x - min < threshold :
        return x + plus
    else :
        return x - min
colors = colorlist ( clr )
c = clr . copy ( )
c . brightness = _wrap ( clr . brightness , 0.5 , 0.2 , 0.3 )
c . saturation = _wrap ( clr . saturation , 0.3 , 0.1 , 0.3 )
colors . append ( c )
c = clr . copy ( )
c . brightness = _wrap ( clr . brightness , 0.2 , 0.2 , 0.6 )
colors . append ( c )
c = clr . copy ( )
c . brightness = max ( 0.2 , clr . brightness + ( 1 - clr . brightness ) * 0.2 )
c . saturation = _wrap ( clr . saturation , 0.3 , 0.1 , 0.3 )
colors . append ( c )
c = clr . copy ( )
c . brightness = _wrap ( clr . brightness , 0.5 , 0.2 , 0.3 )
colors . append ( c )
return colors


signature:
def triad ( clr , angle = 120 ) :
    """Returns a triad of colors .
 The triad is made up of this color and two other colors
 that together make up an equilateral triangle on
 the artistic color wheel ."""

implementation:
clr = color ( clr )
colors = colorlist ( clr )
colors . append ( clr . rotate_ryb ( angle ) . lighten ( 0.1 ) )
colors . append ( clr . rotate_ryb ( - angle ) . lighten ( 0.1 ) )
return colors


signature:
def tetrad ( clr , angle = 90 ) :
    """Returns a tetrad of colors .
 The tetrad is made up of this color and three other colors
 that together make up a cross on the artistic color wheel ."""

implementation:
clr = color ( clr )
colors = colorlist ( clr )
c = clr . rotate_ryb ( angle )
if clr . brightness < 0.5 :
    c . brightness += 0.2
else :
    c . brightness -= - 0.2
colors . append ( c )
c = clr . rotate_ryb ( angle * 2 )
if clr . brightness < 0.5 :
    c . brightness += 0.1
else :
    c . brightness -= - 0.1
colors . append ( c )
colors . append ( clr . rotate_ryb ( angle * 3 ) . lighten ( 0.1 ) )
return colors


signature:
def compound ( clr , flip = False ) :
    """Roughly the complement and some far analogs ."""

implementation:
def _wrap ( x , min , threshold , plus ) :
    if x - min < threshold :
        return x + plus
    else :
        return x - min
d = 1
if flip :
    d = - 1
clr = color ( clr )
colors = colorlist ( clr )
c = clr . rotate_ryb ( 30 * d )
c . brightness = _wrap ( clr . brightness , 0.25 , 0.6 , 0.25 )
colors . append ( c )
c = clr . rotate_ryb ( 30 * d )
c . saturation = _wrap ( clr . saturation , 0.4 , 0.1 , 0.4 )
c . brightness = _wrap ( clr . brightness , 0.4 , 0.2 , 0.4 )
colors . append ( c )
c = clr . rotate_ryb ( 160 * d )
c . saturation = _wrap ( clr . saturation , 0.25 , 0.1 , 0.25 )
c . brightness = max ( 0.2 , clr . brightness )
colors . append ( c )
c = clr . rotate_ryb ( 150 * d )
c . saturation = _wrap ( clr . saturation , 0.1 , 0.8 , 0.1 )
c . brightness = _wrap ( clr . brightness , 0.3 , 0.6 , 0.3 )
colors . append ( c )
c = clr . rotate_ryb ( 150 * d )
c . saturation = _wrap ( clr . saturation , 0.1 , 0.8 , 0.1 )
c . brightness = _wrap ( clr . brightness , 0.4 , 0.2 , 0.4 )
# colors . append ( c )
return colors


signature:
def outline ( path , colors , precision = 0.4 , continuous = True ) :
    """Outlines each contour in a path with the colors in the list .
 Each contour starts with the first color in the list ,
 and ends with the last color in the list .
 Because each line segment is drawn separately ,
 works only with corner - mode transforms ."""

implementation:
# The count of points in a given path / contour .
def _point_count ( path , precision ) :
    return max ( int ( path . length * precision * 0.5 ) , 10 )
# The total count of points in the path .
n = sum ( [ _point_count ( contour , precision ) for contour in path . contours ] )
# For a continuous gradient ,
# we need to calculate a subrange in the list of colors
# for each contour to draw colors from .
contour_i = 0
contour_n = len ( path . contours ) - 1
if contour_n == 0 :
    continuous = False
i = 0
for contour in path . contours :
    if not continuous :
        i = 0
    # The number of points for each contour .
    j = _point_count ( contour , precision )
    first = True
    for pt in contour . points ( j ) :
        if first :
            first = False
        else :
            if not continuous : # If we have a list of 100 colors and 50 points ,
            # point i maps to color i * 2.
                clr = float ( i ) / j * len ( colors )
            else : # In a continuous gradient of 100 colors ,
            # the 2nd contour in a path with 10 contours
            # draws colors between 10-20
                clr = float ( i ) / n * len ( colors ) - 1 * contour_i / contour_n
            _ctx . stroke ( colors [ int ( clr ) ] )
            _ctx . line ( x0 , y0 , pt . x , pt . y )
        x0 = pt . x
        y0 = pt . y
        i += 1
    pt = contour . point ( 0.9999999 )
    # Fix in pathmatics !
    _ctx . line ( x0 , y0 , pt . x , pt . y )
    contour_i += 1


signature:
def guess_name ( clr ) :
    """Guesses the shade and hue name of a color .
 If the given color is named in the named _ colors list , return that name .
 Otherwise guess its nearest hue and shade range ."""

implementation:
clr = Color ( clr )
if clr . is_transparent :
    return "transparent"
if clr . is_black :
    return "black"
if clr . is_white :
    return "white"
if clr . is_black :
    return "black"
for name in named_colors :
    try :
        r , g , b = named_colors [ name ]
    except :
        continue
    if r == clr . r and g == clr . g and b == clr . b :
        return name
for shade in shades :
    if clr in shade :
        return shade . name + " " + clr . nearest_hue ( )
        break
return clr . nearest_hue ( )


signature:
def shader ( x , y , dx , dy , radius = 300 , angle = 0 , spread = 90 ) :
    """Returns a 0.0 - 1.0 brightness adjusted to a light source .
 The light source is positioned at dx , dy .
 The returned float is calculated for x , y position
 ( e . g . an oval at x , y should have this brightness ) .
 The radius influences the strength of the light ,
 angle and spread control the direction of the light ."""

implementation:
if angle != None :
    radius *= 2
# Get the distance and angle between point and light source .
d = sqrt ( ( dx - x ) ** 2 + ( dy - y ) ** 2 )
a = degrees ( atan2 ( dy - y , dx - x ) ) + 180
# If no angle is defined ,
# light is emitted evenly in all directions
# and carries as far as the defined radius
# ( e . g . like a radial gradient ) .
if d <= radius :
    d1 = 1.0 * d / radius
else :
    d1 = 1.0
if angle is None :
    return 1 - d1
# Normalize the light ' s direction and spread
# between 0 and 360.
angle = 360 - angle % 360
spread = max ( 0 , min ( spread , 360 ) )
if spread == 0 :
    return 0.0
# Objects that fall within the spreaded direction
# of the light are illuminated .
d = abs ( a - angle )
if d <= spread / 2 :
    d2 = d / spread + d1
else :
    d2 = 1.0
# Wrapping from 0 to 360:
# a light source with a direction of 10 degrees
# and a spread of 45 degrees illuminates
# objects between 0 and 35 degrees and 350 and 360 degrees .
if 360 - angle <= spread / 2 :
    d = abs ( 360 - angle + a )
    if d <= spread / 2 :
        d2 = d / spread + d1
# Wrapping from 360 to 0.
if angle < spread / 2 :
    d = abs ( 360 + angle - a )
    if d <= spread / 2 :
        d2 = d / spread + d1
return 1 - max ( 0 , min ( d2 , 1 ) )


signature:
def aggregated ( cache = DEFAULT_CACHE ) :
    """A dictionary of all aggregated words .
 They keys in the dictionary correspond to subfolders in the aggregated cache .
 Each key has a list of words . Each of these words is the name of an XML - file
 in the subfolder . The XML - file contains color information harvested from the web
 ( or handmade ) ."""

implementation:
global _aggregated_name , _aggregated_dict
if _aggregated_name != cache :
    _aggregated_name = cache
    _aggregated_dict = { }
    for path in glob ( os . path . join ( cache , "*" ) ) :
        if os . path . isdir ( path ) :
            p = os . path . basename ( path )
            _aggregated_dict [ p ] = glob ( os . path . join ( path , "*" ) )
            _aggregated_dict [ p ] = [ os . path . basename ( f ) [ : - 4 ] for f in _aggregated_dict [ p ] ]
return _aggregated_dict


signature:
def search_engine ( query , top = 5 , service = "google" , license = None , cache = os . path . join ( DEFAULT_CACHE , "google" ) ) :
    """Return a color aggregate from colors and ranges parsed from the web .
 T . De Smedt , http : / / nodebox . net / code / index . php / Prism"""

implementation:
# Check if we have cached information first .
try :
    a = theme ( query , cache = cache )
    return a
except :
    pass
if service == "google" :
    from web import google
    search_engine = google
if service == "yahoo" :
    from web import yahoo
    search_engine = yahoo
    if license :
        yahoo . license_key = license
# Sort all the primary hues ( plus black and white ) for q .
sorted_colors = search_engine . sort ( [ h for h in primary_hues ] + [ "black" , "white" ] , context = query , strict = True , cached = True )
# Sort all the shades ( bright , hard , . . . ) for q .
sorted_shades = search_engine . sort ( [ str ( s ) for s in shades ] , context = query , strict = True , cached = True )
# Reforms ' " black death " ' to ' black ' .
f = lambda x : x . strip ( "\"" ) . split ( ) [ 0 ]
# Take the top most relevant hues .
n2 = sum ( [ w for h , w in sorted_colors [ : top ] ] )
sorted_colors = [ ( color ( f ( h ) ) , w / n2 ) for h , w in sorted_colors [ : top ] ]
# Take the three most relevant shades .
n2 = sum ( [ w for s , w in sorted_shades [ : 3 ] ] )
sorted_shades = [ ( shade ( f ( s ) ) , w / n2 ) for s , w in sorted_shades [ : 3 ] ]
a = theme ( cache = cache )
a . name = query
for clr , w1 in sorted_colors :
    for rng , w2 in sorted_shades :
        a . add_range ( rng , clr , w1 * w2 )
a . _save ( )
return a


signature:
def morguefile ( query , n = 10 , top = 10 ) :
    """Returns a list of colors drawn from a morgueFile image .
 With the Web library installed ,
 downloads a thumbnail from morgueFile and retrieves pixel colors ."""

implementation:
from web import morguefile
images = morguefile . search ( query ) [ : top ]
path = choice ( images ) . download ( thumbnail = True , wait = 10 )
return ColorList ( path , n , name = query )


signature:
def str_to_rgb ( self , str ) :
    """Returns RGB values based on a descriptive string .
 If the given str is a named color , return its RGB values .
 Otherwise , return a random named color that has str
 in its name , or a random named color which name appears in str .
 Specific suffixes ( - ish , - ed , - y and - like ) are recognised
 as well , for example , if you need a random variation of " red "
 you can use reddish ( or greenish , yellowy , etc . )"""

implementation:
str = str . lower ( )
for ch in "_- " :
    str = str . replace ( ch , "" )
# if named _ hues . has _ key ( str ) :
# clr = color ( named _ hues [ str ] , 1 , 1 , mode = " hsb " )
# return clr . r , clr . g , clr . b
if named_colors . has_key ( str ) :
    return named_colors [ str ]
for suffix in [ "ish" , "ed" , "y" , "like" ] :
    str = re . sub ( "(.*?)" + suffix + "$" , "\\1" , str )
str = re . sub ( "(.*?)dd$" , "\\1d" , str )
matches = [ ]
for name in named_colors :
    if name in str or str in name :
        matches . append ( named_colors [ name ] )
if len ( matches ) > 0 :
    return choice ( matches )
return named_colors [ "transparent" ]


signature:
def rotate_ryb ( self , angle = 180 ) :
    """Returns a color rotated on the artistic RYB color wheel .
 An artistic color wheel has slightly different opposites
 ( e . g . purple - yellow instead of purple - lime ) .
 It is mathematically incorrect but generally assumed
 to provide better complementary colors .
 http : / / en . wikipedia . org / wiki / RYB _ color _ model"""

implementation:
h = self . h * 360
angle = angle % 360
# Approximation of Itten ' s RYB color wheel .
# In HSB , colors hues range from 0-360.
# However , on the artistic color wheel these are not evenly distributed .
# The second tuple value contains the actual distribution .
wheel = [ ( 0 , 0 ) , ( 15 , 8 ) , ( 30 , 17 ) , ( 45 , 26 ) , ( 60 , 34 ) , ( 75 , 41 ) , ( 90 , 48 ) , ( 105 , 54 ) , ( 120 , 60 ) , ( 135 , 81 ) , ( 150 , 103 ) , ( 165 , 123 ) , ( 180 , 138 ) , ( 195 , 155 ) , ( 210 , 171 ) , ( 225 , 187 ) , ( 240 , 204 ) , ( 255 , 219 ) , ( 270 , 234 ) , ( 285 , 251 ) , ( 300 , 267 ) , ( 315 , 282 ) , ( 330 , 298 ) , ( 345 , 329 ) , ( 360 , 0 ) ]
# Given a hue , find out under what angle it is
# located on the artistic color wheel .
for i in _range ( len ( wheel ) - 1 ) :
    x0 , y0 = wheel [ i ]
    x1 , y1 = wheel [ i + 1 ]
    if y1 < y0 :
        y1 += 360
    if y0 <= h <= y1 :
        a = 1.0 * x0 + ( x1 - x0 ) * ( h - y0 ) / ( y1 - y0 )
        break
# And the user - given angle ( e . g . complement ) .
a = ( a + angle ) % 360
# For the given angle , find out what hue is
# located there on the artistic color wheel .
for i in _range ( len ( wheel ) - 1 ) :
    x0 , y0 = wheel [ i ]
    x1 , y1 = wheel [ i + 1 ]
    if y1 < y0 :
        y1 += 360
    if x0 <= a <= x1 :
        h = 1.0 * y0 + ( y1 - y0 ) * ( a - x0 ) / ( x1 - x0 )
        break
h = h % 360
return Color ( h / 360 , self . s , self . brightness , self . a , mode = "hsb" , name = "" )


signature:
def nearest_hue ( self , primary = False ) :
    """Returns the name of the nearest named hue .
 For example ,
 if you supply an indigo color ( a color between blue and violet ) ,
 the return value is " violet " . If primary is set to True ,
 the return value is " purple " .
 Primary colors leave out the fuzzy lime , teal ,
 cyan , azure and violet hues ."""

implementation:
if self . is_black :
    return "black"
elif self . is_white :
    return "white"
elif self . is_grey :
    return "grey"
if primary :
    hues = primary_hues
else :
    hues = named_hues . keys ( )
nearest , d = "" , 1.0
for hue in hues :
    if abs ( self . hue - named_hues [ hue ] ) % 1 < d :
        nearest , d = hue , abs ( self . hue - named_hues [ hue ] ) % 1
return nearest


signature:
def blend ( self , clr , factor = 0.5 ) :
    """Returns a mix of two colors ."""

implementation:
r = self . r * ( 1 - factor ) + clr . r * factor
g = self . g * ( 1 - factor ) + clr . g * factor
b = self . b * ( 1 - factor ) + clr . b * factor
a = self . a * ( 1 - factor ) + clr . a * factor
return Color ( r , g , b , a , mode = "rgb" )


signature:
def distance ( self , clr ) :
    """Returns the Euclidean distance between two colors ( 0.0-1.0 ) .
 Consider colors arranged on the color wheel :
 - hue is the angle of a color along the center
 - saturation is the distance of a color from the center
 - brightness is the elevation of a color from the center
 ( i . e . we ' re on color a sphere )"""

implementation:
coord = lambda a , d : ( cos ( radians ( a ) ) * d , sin ( radians ( a ) ) * d )
x0 , y0 = coord ( self . h * 360 , self . s )
x1 , y1 = coord ( clr . h * 360 , clr . s )
z0 = self . brightness
z1 = clr . brightness
d = sqrt ( ( x1 - x0 ) ** 2 + ( y1 - y0 ) ** 2 + ( z1 - z0 ) ** 2 )
return d


signature:
def swatch ( self , x , y , w = 35 , h = 35 , roundness = 0 ) :
    """Rectangle swatch for this color ."""

implementation:
_ctx . fill ( self )
_ctx . rect ( x , y , w , h , roundness )


signature:
def image_to_rgb ( self , path , n = 10 ) :
    """Returns a list of colors based on pixel values in the image .
 The Core Image library must be present to determine pixel colors .
 F . Albers : http : / / nodebox . net / code / index . php / shared _ 2007-06-11-11-37-05"""

implementation:
from PIL import Image
img = Image . open ( path )
p = img . getdata ( )
f = lambda p : choice ( p )
for i in _range ( n ) :
    rgba = f ( p )
    rgba = _list ( rgba )
    if len ( rgba ) == 3 :
        rgba . append ( 255 )
    r , g , b , a = [ v / 255.0 for v in rgba ]
    clr = color ( r , g , b , a , mode = "rgb" )
    self . append ( clr )


signature:
def context_to_rgb ( self , str ) :
    """Returns the colors that have the given word in their context .
 For example , the word " anger " appears
 in black , orange and red contexts ,
 so the list will contain those three colors ."""

implementation:
matches = [ ]
for clr in context :
    tags = context [ clr ]
    for tag in tags :
        if tag . startswith ( str ) or str . startswith ( tag ) :
            matches . append ( clr )
            break
matches = [ color ( name ) for name in matches ]
return matches


signature:
def _context ( self ) :
    """Returns the intersection of each color ' s context .
 Get the nearest named hue of each color ,
 and finds overlapping tags in each hue ' s colors .
 For example , a list containing yellow , deeppink and olive
 yields : femininity , friendship , happiness , joy ."""

implementation:
tags1 = None
for clr in self :
    overlap = [ ]
    if clr . is_black :
        name = "black"
    elif clr . is_white :
        name = "white"
    elif clr . is_grey :
        name = "grey"
    else :
        name = clr . nearest_hue ( primary = True )
    if name == "orange" and clr . brightness < 0.6 :
        name = "brown"
    tags2 = context [ name ]
    if tags1 is None :
        tags1 = tags2
    else :
        for tag in tags2 :
            if tag in tags1 :
                if tag not in overlap :
                    overlap . append ( tag )
        tags1 = overlap
overlap . sort ( )
return overlap


signature:
def copy ( self ) :
    """Returns a deep copy of the list ."""

implementation: return ColorList ( [ color ( clr . r , clr . g , clr . b , clr . a , mode = "rgb" ) for clr in self ] , name = self . name , tags = self . tags )


signature:
def _darkest ( self ) :
    """Returns the darkest color from the list .
 Knowing the contrast between a light and a dark swatch
 can help us decide how to display readable typography ."""

implementation:
min , n = ( 1.0 , 1.0 , 1.0 ) , 3.0
for clr in self :
    if clr . r + clr . g + clr . b < n :
        min , n = clr , clr . r + clr . g + clr . b
return min


signature:
def _average ( self ) :
    """Returns one average color for the colors in the list ."""

implementation:
r , g , b , a = 0 , 0 , 0 , 0
for clr in self :
    r += clr . r
    g += clr . g
    b += clr . b
    a += clr . alpha
r /= len ( self )
g /= len ( self )
b /= len ( self )
a /= len ( self )
return color ( r , g , b , a , mode = "rgb" )


signature:
def sort_by_distance ( self , reversed = False ) :
    """Returns a list with the smallest distance between two neighboring colors .
 The algorithm has a factorial complexity so it may run slow ."""

implementation:
if len ( self ) == 0 :
    return ColorList ( )
# Find the darkest color in the list .
root = self [ 0 ]
for clr in self [ 1 : ] :
    if clr . brightness < root . brightness :
        root = clr
# Remove the darkest color from the stack ,
# put it in the sorted list as starting element .
stack = [ clr for clr in self ]
stack . remove ( root )
sorted = [ root ]
# Now find the color in the stack closest to that color .
# Take this color from the stack and add it to the sorted list .
# Now find the color closest to that color , etc .
while len ( stack ) > 1 :
    closest , distance = stack [ 0 ] , stack [ 0 ] . distance ( sorted [ - 1 ] )
    for clr in stack [ 1 : ] :
        d = clr . distance ( sorted [ - 1 ] )
        if d < distance :
            closest , distance = clr , d
    stack . remove ( closest )
    sorted . append ( closest )
sorted . append ( stack [ 0 ] )
if reversed :
    _list . reverse ( sorted )
return ColorList ( sorted )


signature:
def _sorted_copy ( self , comparison , reversed = False ) :
    """Returns a sorted copy with the colors arranged according to the given comparison ."""

implementation:
sorted = self . copy ( )
_list . sort ( sorted , comparison )
if reversed :
    _list . reverse ( sorted )
return sorted


signature:
def cluster_sort ( self , cmp1 = "hue" , cmp2 = "brightness" , reversed = False , n = 12 ) :
    """Sorts the list by cmp1 , then cuts it into n pieces which are sorted by cmp2.
 If you want to cluster by hue , use n = 12 ( since there are 12 primary / secondary hues ) .
 The resulting list will not contain n even slices :
 n is used rather to slice up the cmp1 property of the colors ,
 e . g . cmp1 = brightness and n = 3 will cluster colors by brightness > = 0.66 , 0.33 , 0.0"""

implementation:
sorted = self . sort ( cmp1 )
clusters = ColorList ( )
d = 1.0
i = 0
for j in _range ( len ( sorted ) ) :
    if getattr ( sorted [ j ] , cmp1 ) < d :
        clusters . extend ( sorted [ i : j ] . sort ( cmp2 ) )
        d -= 1.0 / n
        i = j
clusters . extend ( sorted [ i : ] . sort ( cmp2 ) )
if reversed :
    _list . reverse ( clusters )
return clusters


signature:
def reverse ( self ) :
    """Returns a reversed copy of the list ."""

implementation:
colors = ColorList . copy ( self )
_list . reverse ( colors )
return colors


signature:
def repeat ( self , n = 2 , oscillate = False , callback = None ) :
    """Returns a list that is a repetition of the given list .
 When oscillate is True ,
 moves from the end back to the beginning ,
 and then from the beginning to the end , and so on ."""

implementation:
colorlist = ColorList ( )
colors = ColorList . copy ( self )
for i in _range ( n ) :
    colorlist . extend ( colors )
    if oscillate :
        colors = colors . reverse ( )
    if callback :
        colors = callback ( colors )
return colorlist


signature:
def swatch ( self , x , y , w = 35 , h = 35 , padding = 0 , roundness = 0 ) :
    """Rectangle swatches for all the colors in the list ."""

implementation:
for clr in self :
    clr . swatch ( x , y , w , h , roundness )
    y += h + padding


signature:
def swarm ( self , x , y , r = 100 ) :
    """Fancy random ovals for all the colors in the list ."""

implementation:
sc = _ctx . stroke ( 0 , 0 , 0 , 0 )
sw = _ctx . strokewidth ( 0 )
_ctx . push ( )
_ctx . transform ( _ctx . CORNER )
_ctx . translate ( x , y )
for i in _range ( r * 3 ) :
    clr = choice ( self ) . copy ( )
    clr . alpha -= 0.5 * random ( )
    _ctx . fill ( clr )
    clr = choice ( self )
    _ctx . stroke ( clr )
    _ctx . strokewidth ( 10 * random ( ) )
    _ctx . rotate ( 360 * random ( ) )
    r2 = r * 0.5 * random ( )
    _ctx . oval ( r * random ( ) , 0 , r2 , r2 )
_ctx . pop ( )
_ctx . strokewidth ( sw )
if sc is None :
    _ctx . nostroke ( )
else :
    _ctx . stroke ( sc )


signature:
def _interpolate ( self , colors , n = 100 ) :
    """Returns intermediary colors for given list of colors ."""

implementation:
gradient = [ ]
for i in _range ( n ) :
    l = len ( colors ) - 1
    x = int ( 1.0 * i / n * l )
    x = min ( x + 0 , l )
    y = min ( x + 1 , l )
    base = 1.0 * n / l * x
    d = ( i - base ) / ( 1.0 * n / l )
    r = colors [ x ] . r * ( 1 - d ) + colors [ y ] . r * d
    g = colors [ x ] . g * ( 1 - d ) + colors [ y ] . g * d
    b = colors [ x ] . b * ( 1 - d ) + colors [ y ] . b * d
    a = colors [ x ] . a * ( 1 - d ) + colors [ y ] . a * d
    gradient . append ( color ( r , g , b , a , mode = "rgb" ) )
gradient . append ( colors [ - 1 ] )
return gradient


signature:
def _cache ( self ) :
    """Populates the list with a number of gradient colors .
 The list has Gradient . steps colors that interpolate between
 the fixed base Gradient . colors .
 The spread parameter controls the midpoint of the gradient ,
 you can shift it right and left . A separate gradient is
 calculated for each half and then glued together ."""

implementation:
n = self . steps
# Only one color in base list .
if len ( self . _colors ) == 1 :
    ColorList . __init__ ( self , [ self . _colors [ 0 ] for i in _range ( n ) ] )
    return
# Expand the base list so we can chop more accurately .
colors = self . _interpolate ( self . _colors , 40 )
# Chop into left half and right half .
# Make sure their ending and beginning match colors .
left = colors [ : len ( colors ) / 2 ]
right = colors [ len ( colors ) / 2 : ]
left . append ( right [ 0 ] )
right . insert ( 0 , left [ - 1 ] )
# Calculate left and right gradient proportionally to spread .
gradient = self . _interpolate ( left , int ( n * self . spread ) ) [ : - 1 ]
gradient . extend ( self . _interpolate ( right , n - int ( n * self . spread ) ) [ 1 : ] )
if self . spread > 1 :
    gradient = gradient [ : n ]
if self . spread < 0 :
    gradient = gradient [ - n : ]
ColorList . __init__ ( self , gradient )


signature:
def copy ( self , clr = None , d = 0.0 ) :
    """Returns a copy of the range .
 Optionally , supply a color to get a range copy
 limited to the hue of that color ."""

implementation:
cr = ColorRange ( )
cr . name = self . name
cr . h = deepcopy ( self . h )
cr . s = deepcopy ( self . s )
cr . b = deepcopy ( self . b )
cr . a = deepcopy ( self . a )
cr . grayscale = self . grayscale
if not self . grayscale :
    cr . black = self . black . copy ( )
    cr . white = self . white . copy ( )
if clr != None :
    cr . h , cr . a = clr . h + d * ( random ( ) * 2 - 1 ) , clr . a
return cr


signature:
def color ( self , clr = None , d = 0.035 ) :
    """Returns a color with random values in the defined h , s b , a ranges .
 If a color is given , use that color ' s hue and alpha ,
 and generate its saturation and brightness from the shade .
 The hue is varied with the given d .
 In this way you could have a " warm " color range
 that returns all kinds of warm colors .
 When a red color is given as parameter it would generate
 all kinds of warm red colors ."""

implementation:
# Revert to grayscale for black , white and grey hues .
if clr != None and not isinstance ( clr , Color ) :
    clr = color ( clr )
if clr != None and not self . grayscale :
    if clr . is_black :
        return self . black . color ( clr , d )
    if clr . is_white :
        return self . white . color ( clr , d )
    if clr . is_grey :
        return choice ( ( self . black . color ( clr , d ) , self . white . color ( clr , d ) ) )
h , s , b , a = self . h , self . s , self . b , self . a
if clr != None :
    h , a = clr . h + d * ( random ( ) * 2 - 1 ) , clr . a
hsba = [ ]
for v in [ h , s , b , a ] :
    if isinstance ( v , _list ) :
        min , max = choice ( v )
    elif isinstance ( v , tuple ) :
        min , max = v
    else :
        min , max = v , v
    hsba . append ( min + ( max - min ) * random ( ) )
h , s , b , a = hsba
return color ( h , s , b , a , mode = "hsb" )


signature:
def contains ( self , clr ) :
    """Returns True if the given color is part of this color range .
 Check whether each h , s , b , a component of the color
 falls within the defined range for that component .
 If the given color is grayscale ,
 checks against the definitions for black and white ."""

implementation:
if not isinstance ( clr , Color ) :
    return False
if not isinstance ( clr , _list ) :
    clr = [ clr ]
for clr in clr :
    if clr . is_grey and not self . grayscale :
        return ( self . black . contains ( clr ) or self . white . contains ( clr ) )
    for r , v in [ ( self . h , clr . h ) , ( self . s , clr . s ) , ( self . b , clr . brightness ) , ( self . a , clr . a ) ] :
        if isinstance ( r , _list ) :
            pass
        elif isinstance ( r , tuple ) :
            r = [ r ]
        else :
            r = [ ( r , r ) ]
        for min , max in r :
            if not ( min <= v <= max ) :
                return False
return True


signature:
def _weight_by_hue ( self ) :
    """Returns a list of ( hue , ranges , total weight , normalized total weight ) - tuples .
 ColorTheme is made up out of ( color , range , weight ) tuples .
 For consistency with XML - output in the old Prism format
 ( i . e . < color > s made up of < shade > s ) we need a group
 weight per different hue .
 The same is true for the swatch ( ) draw method .
 Hues are grouped as a single unit ( e . g . dark red , intense red , weak red )
 after which the dimensions ( rows / columns ) is determined ."""

implementation:
grouped = { }
weights = [ ]
for clr , rng , weight in self . ranges :
    h = clr . nearest_hue ( primary = False )
    if grouped . has_key ( h ) :
        ranges , total_weight = grouped [ h ]
        ranges . append ( ( clr , rng , weight ) )
        total_weight += weight
        grouped [ h ] = ( ranges , total_weight )
    else :
        grouped [ h ] = ( [ ( clr , rng , weight ) ] , weight )
# Calculate the normalized ( 0.0-1.0 ) weight for each hue ,
# and transform the dictionary to a list .
s = 1.0 * sum ( [ w for r , w in grouped . values ( ) ] )
grouped = [ ( grouped [ h ] [ 1 ] , grouped [ h ] [ 1 ] / s , h , grouped [ h ] [ 0 ] ) for h in grouped ]
grouped . sort ( )
grouped . reverse ( )
return grouped


signature:
def _xml ( self ) :
    """Returns the color information as XML .
 The XML has the following structure :
 < colors query = " " >
 < color name = " " weight = " " / >
 < rgb r = " " g = " " b = " " / >
 < shade name = " " weight = " " / >
 < / color >
 < / colors >
 Notice that ranges are stored by name and retrieved in the _ load ( )
 method with the shade ( ) command - and are thus expected to be
 shades ( e . g . intense , warm , . . . ) unless the shade ( ) command would
 return any custom ranges as well . This can be done by appending custom
 ranges to the shades list ."""

implementation:
grouped = self . _weight_by_hue ( )
xml = "<colors query=\"" + self . name + "\" tags=\"" + ", " . join ( self . tags ) + "\">\n\n"
for total_weight , normalized_weight , hue , ranges in grouped :
    if hue == self . blue :
        hue = "blue"
    clr = color ( hue )
    xml += "\t<color name=\"" + clr . name + "\" weight=\"" + str ( normalized_weight ) + "\">\n "
    xml += "\t\t<rgb r=\"" + str ( clr . r ) + "\" g=\"" + str ( clr . g ) + "\" "
    xml += "b=\"" + str ( clr . b ) + "\" a=\"" + str ( clr . a ) + "\" />\n "
    for clr , rng , wgt in ranges :
        xml += "\t\t<shade name=\"" + str ( rng ) + "\" weight=\"" + str ( wgt / total_weight ) + "\" />\n "
    xml = xml . rstrip ( " " ) + "\t</color>\n\n"
xml += "</colors>"
return xml


signature:
def _save ( self ) :
    """Saves the color information in the cache as XML ."""

implementation:
if not os . path . exists ( self . cache ) :
    os . makedirs ( self . cache )
path = os . path . join ( self . cache , self . name + ".xml" )
f = open ( path , "w" )
f . write ( self . xml )
f . close ( )


signature:
def _load ( self , top = 5 , blue = "blue" , archive = None , member = None ) :
    """Loads a theme from aggregated web data .
 The data must be old - style Prism XML : < color > s consisting of < shade > s .
 Colors named " blue " will be overridden with the blue parameter .
 archive can be a file like object ( e . g . a ZipFile )
 and will be used along with ' member ' if specified ."""

implementation:
if archive is None :
    path = os . path . join ( self . cache , self . name + ".xml" )
    xml = open ( path ) . read ( )
else :
    assert member is not None
    xml = archive . read ( member )
dom = parseString ( xml ) . documentElement
attr = lambda e , a : e . attributes [ a ] . value
for e in dom . getElementsByTagName ( "color" ) [ : top ] :
    w = float ( attr ( e , "weight" ) )
    try :
        rgb = e . getElementsByTagName ( "rgb" ) [ 0 ]
        clr = color ( float ( attr ( rgb , "r" ) ) , float ( attr ( rgb , "g" ) ) , float ( attr ( rgb , "b" ) ) , float ( attr ( rgb , "a" ) ) , mode = "rgb" )
        try :
            clr . name = attr ( e , "name" )
            if clr . name == "blue" :
                clr = color ( blue )
        except :
            pass
    except :
        name = attr ( e , "name" )
        if name == "blue" :
            name = blue
        clr = color ( name )
    for s in e . getElementsByTagName ( "shade" ) :
        self . ranges . append ( ( clr , shade ( attr ( s , "name" ) ) , w * float ( attr ( s , "weight" ) ) ) )


signature:
def color ( self , d = 0.035 ) :
    """Returns a random color within the theme .
 Fetches a random range ( the weight is taken into account ,
 so ranges with a bigger weight have a higher chance of propagating )
 and hues it with the associated color ."""

implementation:
s = sum ( [ w for clr , rng , w in self . ranges ] )
r = random ( )
for clr , rng , weight in self . ranges :
    if weight / s >= r :
        break
    r -= weight / s
return rng ( clr , d )


signature:
def colors ( self , n = 10 , d = 0.035 ) :
    """Returns a number of random colors from the theme ."""

implementation:
s = sum ( [ w for clr , rng , w in self . ranges ] )
colors = colorlist ( )
for i in _range ( n ) :
    r = random ( )
    for clr , rng , weight in self . ranges :
        if weight / s >= r :
            break
        r -= weight / s
    colors . append ( rng ( clr , d ) )
return colors


signature:
def recombine ( self , other , d = 0.7 ) :
    """Genetic recombination of two themes using cut and splice technique ."""

implementation:
a , b = self , other
d1 = max ( 0 , min ( d , 1 ) )
d2 = d1
c = ColorTheme ( name = a . name [ : int ( len ( a . name ) * d1 ) ] + b . name [ int ( len ( b . name ) * d2 ) : ] , ranges = a . ranges [ : int ( len ( a . ranges ) * d1 ) ] + b . ranges [ int ( len ( b . ranges ) * d2 ) : ] , top = a . top , cache = os . path . join ( DEFAULT_CACHE , "recombined" ) , blue = a . blue , length = a . length * d1 + b . length * d2 )
c . tags = a . tags [ : int ( len ( a . tags ) * d1 ) ]
c . tags += b . tags [ int ( len ( b . tags ) * d2 ) : ]
return c


signature:
def swatch ( self , x , y , w = 35 , h = 35 , padding = 4 , roundness = 0 , n = 12 , d = 0.035 , grouped = None ) :
    """Draws a weighted swatch with approximately n columns and rows .
 When the grouped parameter is True , colors are grouped in blocks of the same hue
 ( also see the _ weight _ by _ hue ( ) method ) ."""

implementation:
if grouped is None : # should be True or False
    grouped = self . group_swatches
# If we dont ' t need to make groups ,
# just display an individual column for each weight
# in the ( color , range , weight ) tuples .
if not grouped :
    s = sum ( [ wgt for clr , rng , wgt in self . ranges ] )
    for clr , rng , wgt in self . ranges :
        cols = max ( 1 , int ( wgt / s * n ) )
        for i in _range ( cols ) :
            rng . colors ( clr , n = n , d = d ) . swatch ( x , y , w , h , padding = padding , roundness = roundness )
            x += w + padding
    return x , y + n * ( h + padding )
# When grouped , combine hues and display them
# in batches of rows , then moving on to the next hue .
grouped = self . _weight_by_hue ( )
for total_weight , normalized_weight , hue , ranges in grouped :
    dy = y
    rc = 0
    for clr , rng , weight in ranges :
        dx = x
        cols = int ( normalized_weight * n )
        cols = max ( 1 , min ( cols , n - len ( grouped ) ) )
        if clr . name == "black" :
            rng = rng . black
        if clr . name == "white" :
            rng = rng . white
        for i in _range ( cols ) :
            rows = int ( weight / total_weight * n )
            rows = max ( 1 , rows )
            # Each column should add up to n rows ,
            # if not due to rounding errors , add a row at the bottom .
            if ( clr , rng , weight ) == ranges [ - 1 ] and rc + rows < n :
                rows += 1
            rng . colors ( clr , n = rows , d = d ) . swatch ( dx , dy , w , h , padding = padding , roundness = roundness )
            dx += w + padding
        dy += ( w + padding ) * rows
        # + padding
        rc = rows
    x += ( w + padding ) * cols + padding
return x , dy


signature:
def fseq ( self , client , message ) :
    """fseq messages associate a unique frame id with a set of set
 and alive messages"""

implementation:
client . last_frame = client . current_frame
client . current_frame = message [ 3 ]


signature:
def objs ( self ) :
    """Returns a generator list of tracked objects which are recognized with
 this profile and are in the current session ."""

implementation:
for obj in self . objects . itervalues ( ) :
    if obj . sessionid in self . sessions :
        yield obj


signature:
def _append_element ( self , render_func , pe ) :
    '''Append a render function and the parameters to pass
 an equivilent PathElement , or the PathElement itself .'''

implementation:
self . _render_funcs . append ( render_func )
self . _elements . append ( pe )


signature:
def _get_bounds ( self ) :
    '''Return cached bounds of this Grob .
 If bounds are not cached , render to a meta surface , and
 keep the meta surface and bounds cached .'''

implementation:
if self . _bounds :
    return self . _bounds
record_surface = cairo . RecordingSurface ( cairo . CONTENT_COLOR_ALPHA , ( - 1 , - 1 , 1 , 1 ) )
dummy_ctx = cairo . Context ( record_surface )
self . _traverse ( dummy_ctx )
self . _bounds = dummy_ctx . path_extents ( )
return self . _bounds


signature:
def contains ( self , x , y ) :
    '''Return cached bounds of this Grob .
 If bounds are not cached , render to a meta surface , and
 keep the meta surface and bounds cached .'''

implementation:
if self . _bounds :
    return self . _bounds
record_surface = cairo . RecordingSurface ( cairo . CONTENT_COLOR_ALPHA , ( - 1 , - 1 , 1 , 1 ) )
dummy_ctx = cairo . Context ( record_surface )
self . _traverse ( dummy_ctx )
in_fill = dummy_ctx . in_fill ( x , y )
return in_fill


signature:
def _get_center ( self ) :
    '''Return cached bounds of this Grob .
 If bounds are not cached , render to a meta surface , and
 keep the meta surface and bounds cached .'''

implementation:
if self . _center :
    return self . _center
# get the center point
( x1 , y1 , x2 , y2 ) = self . _get_bounds ( )
x = ( x1 + x2 ) / 2
y = ( y1 + y2 ) / 2
center = self . _center = x , y
# TODO Cache function that draws using the RecordingSurface
# Save the context or surface ( without the bounding box strokecolor )
# to optimise drawing
return center


signature:
def _get_contours ( self ) :
    """Returns a list of contours in the path , as BezierPath objects .
 A contour is a sequence of lines and curves separated from the next contour by a MOVETO .
 For example , the glyph " o " has two contours : the inner circle and the outer circle ."""

implementation:
# Originally from nodebox - gl
contours = [ ]
current_contour = None
empty = True
for i , el in enumerate ( self . _get_elements ( ) ) :
    if el . cmd == MOVETO :
        if not empty :
            contours . append ( current_contour )
        current_contour = BezierPath ( self . _bot )
        current_contour . moveto ( el . x , el . y )
        empty = True
    elif el . cmd == LINETO :
        empty = False
        current_contour . lineto ( el . x , el . y )
    elif el . cmd == CURVETO :
        empty = False
        current_contour . curveto ( el . c1x , el . c1y , el . c2x , el . c2y , el . x , el . y )
    elif el . cmd == CLOSE :
        current_contour . closepath ( )
if not empty :
    contours . append ( current_contour )
return contours


signature:
def _locate ( self , t , segments = None ) :
    """Locates t on a specific segment in the path .
 Returns ( index , t , PathElement )
 A path is a combination of lines and curves ( segments ) .
 The returned index indicates the start of the segment that contains point t .
 The returned t is the absolute time on that segment ,
 in contrast to the relative t on the whole of the path .
 The returned point is the last MOVETO , any subsequent CLOSETO after i closes to that point .
 When you supply the list of segment lengths yourself , as returned from length ( path , segmented = True ) ,
 point ( ) works about thirty times faster in a for - loop since it doesn ' t need to recalculate
 the length during each iteration ."""

implementation:
# Originally from nodebox - gl
if segments is None :
    segments = self . _segment_lengths ( relative = True )
if len ( segments ) == 0 :
    raise PathError , "The given path is empty"
for i , el in enumerate ( self . _get_elements ( ) ) :
    if i == 0 or el . cmd == MOVETO :
        closeto = Point ( el . x , el . y )
    if t <= segments [ i ] or i == len ( segments ) - 1 :
        break
    else :
        t -= segments [ i ]
try :
    t /= segments [ i ]
except ZeroDivisionError :
    pass
if i == len ( segments ) - 1 and segments [ i ] == 0 :
    i -= 1
return ( i , t , closeto )


signature:
def point ( self , t , segments = None ) :
    """Returns the PathElement at time t ( 0.0-1.0 ) on the path .
 Returns coordinates for point at t on the path .
 Gets the length of the path , based on the length of each curve and line in the path .
 Determines in what segment t falls . Gets the point on that segment .
 When you supply the list of segment lengths yourself , as returned from length ( path , segmented = True ) ,
 point ( ) works about thirty times faster in a for - loop since it doesn ' t need to recalculate
 the length during each iteration ."""

implementation:
# Originally from nodebox - gl
if len ( self . _elements ) == 0 :
    raise PathError ( "The given path is empty" )
if self . _segments is None :
    self . _segments = self . _get_length ( segmented = True , precision = 10 )
i , t , closeto = self . _locate ( t , segments = self . _segments )
x0 , y0 = self [ i ] . x , self [ i ] . y
p1 = self [ i + 1 ]
if p1 . cmd == CLOSE :
    x , y = self . _linepoint ( t , x0 , y0 , closeto . x , closeto . y )
    return PathElement ( LINETO , x , y )
elif p1 . cmd in ( LINETO , MOVETO ) :
    x1 , y1 = p1 . x , p1 . y
    x , y = self . _linepoint ( t , x0 , y0 , x1 , y1 )
    return PathElement ( LINETO , x , y )
elif p1 . cmd == CURVETO : # Note : the handles need to be interpreted differenty than in a BezierPath .
# In a BezierPath , ctrl1 is how the curve started , and ctrl2 how it arrives in this point .
# Here , ctrl1 is how the curve arrives , and ctrl2 how it continues to the next point .
    x3 , y3 , x1 , y1 , x2 , y2 = p1 . x , p1 . y , p1 . ctrl1 . x , p1 . ctrl1 . y , p1 . ctrl2 . x , p1 . ctrl2 . y
    x , y , c1x , c1y , c2x , c2y = self . _curvepoint ( t , x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 )
    return PathElement ( CURVETO , c1x , c1y , c2x , c2y , x , y )
else :
    raise PathError ( "Unknown cmd '%s' for p1 %s" % ( p1 . cmd , p1 ) )


signature:
def points ( self , amount = 100 , start = 0.0 , end = 1.0 , segments = None ) :
    """Returns an iterator with a list of calculated points for the path .
 To omit the last point on closed paths : end = 1-1.0 / amount"""

implementation:
# Originally from nodebox - gl
if len ( self . _elements ) == 0 :
    raise PathError ( "The given path is empty" )
n = end - start
d = n
if amount > 1 : # The delta value is divided by amount - 1 , because we also want the last point ( t = 1.0)
# If we don ' t use amount - 1 , we fall one point short of the end .
# If amount = 4 , we want the point at t 0.0 , 0.33 , 0.66 and 1.0.
# If amount = 2 , we want the point at t 0.0 and 1.0.
    d = float ( n ) / ( amount - 1 )
for i in xrange ( int ( amount ) ) :
    yield self . point ( start + d * i , segments )


signature:
def _linepoint ( self , t , x0 , y0 , x1 , y1 ) :
    """Returns coordinates for point at t on the line .
 Calculates the coordinates of x and y for a point at t on a straight line .
 The t parameter is a number between 0.0 and 1.0,
 x0 and y0 define the starting point of the line ,
 x1 and y1 the ending point of the line ."""

implementation:
# Originally from nodebox - gl
out_x = x0 + t * ( x1 - x0 )
out_y = y0 + t * ( y1 - y0 )
return ( out_x , out_y )


signature:
def _linelength ( self , x0 , y0 , x1 , y1 ) :
    """Returns the length of the line ."""

implementation:
# Originally from nodebox - gl
a = pow ( abs ( x0 - x1 ) , 2 )
b = pow ( abs ( y0 - y1 ) , 2 )
return sqrt ( a + b )


signature:
def _curvepoint ( self , t , x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , handles = False ) :
    """Returns coordinates for point at t on the spline .
 Calculates the coordinates of x and y for a point at t on the cubic bezier spline ,
 and its control points , based on the de Casteljau interpolation algorithm .
 The t parameter is a number between 0.0 and 1.0,
 x0 and y0 define the starting point of the spline ,
 x1 and y1 its control point ,
 x3 and y3 the ending point of the spline ,
 x2 and y2 its control point .
 If the handles parameter is set , returns not only the point at t ,
 but the modified control points of p0 and p3 should this point split the path as well ."""

implementation:
# Originally from nodebox - gl
mint = 1 - t
x01 = x0 * mint + x1 * t
y01 = y0 * mint + y1 * t
x12 = x1 * mint + x2 * t
y12 = y1 * mint + y2 * t
x23 = x2 * mint + x3 * t
y23 = y2 * mint + y3 * t
out_c1x = x01 * mint + x12 * t
out_c1y = y01 * mint + y12 * t
out_c2x = x12 * mint + x23 * t
out_c2y = y12 * mint + y23 * t
out_x = out_c1x * mint + out_c2x * t
out_y = out_c1y * mint + out_c2y * t
if not handles :
    return ( out_x , out_y , out_c1x , out_c1y , out_c2x , out_c2y )
else :
    return ( out_x , out_y , out_c1x , out_c1y , out_c2x , out_c2y , x01 , y01 , x23 , y23 )


signature:
def _curvelength ( self , x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , n = 20 ) :
    """Returns the length of the spline .
 Integrates the estimated length of the cubic bezier spline defined by x0 , y0 , . . . x3 , y3,
 by adding the lengths of lineair lines between points at t .
 The number of points is defined by n
 ( n = 10 would add the lengths of lines between 0.0 and 0.1 , between 0.1 and 0.2 , and so on ) .
 The default n = 20 is fine for most cases , usually resulting in a deviation of less than 0.01."""

implementation:
# Originally from nodebox - gl
length = 0
xi = x0
yi = y0
for i in range ( n ) :
    t = 1.0 * ( i + 1 ) / n
    pt_x , pt_y , pt_c1x , pt_c1y , pt_c2x , pt_c2y = self . _curvepoint ( t , x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 )
    c = sqrt ( pow ( abs ( xi - pt_x ) , 2 ) + pow ( abs ( yi - pt_y ) , 2 ) )
    length += c
    xi = pt_x
    yi = pt_y
return length


signature:
def _segment_lengths ( self , relative = False , n = 20 ) :
    """Returns a list with the lengths of each segment in the path ."""

implementation:
# From nodebox _ gl
lengths = [ ]
first = True
for el in self . _get_elements ( ) :
    if first is True :
        close_x , close_y = el . x , el . y
        first = False
    elif el . cmd == MOVETO :
        close_x , close_y = el . x , el . y
        lengths . append ( 0.0 )
    elif el . cmd == CLOSE :
        lengths . append ( self . _linelength ( x0 , y0 , close_x , close_y ) )
    elif el . cmd == LINETO :
        lengths . append ( self . _linelength ( x0 , y0 , el . x , el . y ) )
    elif el . cmd == CURVETO :
        x3 , y3 , x1 , y1 , x2 , y2 = el . x , el . y , el . c1x , el . c1y , el . c2x , el . c2y
        # ( el . c1x , el . c1y , el . c2x , el . c2y , el . x , el . y )
        lengths . append ( self . _curvelength ( x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , n ) )
    if el . cmd != CLOSE :
        x0 = el . x
        y0 = el . y
if relative :
    length = sum ( lengths )
    try : # Relative segment lengths ' sum is 1.0.
        return map ( lambda l : l / length , lengths )
    except ZeroDivisionError : # If the length is zero , just return zero for all segments
        return [ 0.0 ] * len ( lengths )
else :
    return lengths


signature:
def _get_length ( self , segmented = False , precision = 10 ) :
    """Returns the length of the path .
 Calculates the length of each spline in the path , using n as a number of points to measure .
 When segmented is True , returns a list containing the individual length of each spline
 as values between 0.0 and 1.0 , defining the relative length of each spline
 in relation to the total path length ."""

implementation:
# Originally from nodebox - gl
if not segmented :
    return sum ( self . _segment_lengths ( n = precision ) , 0.0 )
else :
    return self . _segment_lengths ( relative = True , n = precision )


signature:
def _get_elements ( self ) :
    '''Yields all elements as PathElements'''

implementation:
for index , el in enumerate ( self . _elements ) :
    if isinstance ( el , tuple ) :
        el = PathElement ( * el )
        self . _elements [ index ] = el
    yield el


signature:
def depth_first_search ( root , visit = lambda node : False , traversable = lambda node , edge : True ) :
    """Simple , multi - purpose depth - first search .
 Visits all the nodes connected to the root , depth - first .
 The visit function is called on each node .
 Recursion will stop if it returns True , and ubsequently dfs ( ) will return True .
 The traversable function takes the current node and edge ,
 and returns True if we are allowed to follow this connection to the next node .
 For example , the traversable for directed edges is follows :
 lambda node , edge : node = = edge . node1
 Note : node . _ visited is expected to be False for all nodes ."""

implementation:
stop = visit ( root )
root . _visited = True
for node in root . links :
    if stop :
        return True
    if not traversable ( root , root . links . edge ( node ) ) :
        continue
    if not node . _visited :
        stop = depth_first_search ( node , visit , traversable )
return stop