From 06caff500b327df71eeec3cfe5a79e36d75e09d7 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Wed, 11 Feb 2009 21:47:58 +0000 Subject: Merge collab branch * reform gui to use space more effectively * add collab code --- diff --git a/README b/README index e69de29..40f2941 100644 --- a/README +++ b/README @@ -0,0 +1,7 @@ +linear interpolation: +this is what to do to get intermediate points + +skeletal animation: +what the program is doing + + diff --git a/todo.txt b/TODO index ea999b1..ea999b1 100644 --- a/todo.txt +++ b/TODO diff --git a/activity.py b/activity.py new file mode 100644 index 0000000..e209e1a --- /dev/null +++ b/activity.py @@ -0,0 +1,207 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk +from gettext import gettext as _ + +from sugar.activity.activity import Activity, ActivityToolbox +from sugar.graphics.toggletoolbutton import ToggleToolButton +from sugar.graphics.toolbutton import ToolButton + +import model +import montage +import lessons +from messenger import Messenger, SERVICE +from shared import SharedActivity +from theme import * +from utils import * + +class flipsticksActivity(SharedActivity): + def __init__(self, handle): + self.notebook = gtk.Notebook() + SharedActivity.__init__(self, self.notebook, SERVICE, handle) + + self.connect('tube', self._tube_cb) + + self.notebook.show() + self.notebook.props.show_border = False + self.notebook.props.show_tabs = False + + self.montage = montage.View(self) + self.notebook.append_page(self.montage) + self.lessons = lessons.View() + self.lessons.show() + self.notebook.append_page(self.lessons) + + toolbox = ActivityToolbox(self) + toolbox.show() + toolbox.connect('current-toolbar-changed', self._toolbar_changed_cb) + self.set_toolbox(toolbox) + + montage_bar = MontageToolbar(self.montage) + montage_bar.show() + toolbox.add_toolbar(_('Montage'), montage_bar) + + lessons_bar = LessonsToolbar() + lessons_bar.show() + toolbox.add_toolbar(_('Lessons'), lessons_bar) + + toolbox.set_current_toolbar(1) + + def read_file(self, filepath): + model.load(filepath) + self.montage.restore() + + def write_file(self, filepath): + model.save(filepath) + + def _toolbar_changed_cb(self, widget, index): + if index == 2: + self.notebook.set_current_page(1) + else: + self.notebook.set_current_page(0) + + def _tube_cb(self, activity, tube_conn, initiating): + self.messenger = Messenger(tube_conn, initiating, self.montage) + +class MontageToolbar(gtk.Toolbar): + def __init__(self, montage): + gtk.Toolbar.__init__(self) + self.montage = montage + + # edit buttons + + setframe = ToolButton('dialog-ok') + setframe.connect('clicked', self._setframe_cb) + setframe.set_tooltip(_('Set frame')) + self.insert(setframe, -1) + + clearframe = ToolButton('gtk-delete') + clearframe.connect('clicked', self._clearframe_cb) + clearframe.set_tooltip(_('Clear frame')) + self.insert(clearframe, -1) + + resetframe = ToolButton('gtk-cancel') + resetframe.connect('clicked', self._resetframe_cb) + resetframe.set_tooltip(_('Reset')) + self.insert(resetframe, -1) + + separator = gtk.SeparatorToolItem() + self.insert(separator,-1) + + # play/pause buttons + + play_img_1 = gtk.Image() + play_img_1.set_from_icon_name('media-playback-start-back', + gtk.ICON_SIZE_LARGE_TOOLBAR) + pause_img_1 = gtk.Image() + pause_img_1.set_from_icon_name('media-playback-pause', + gtk.ICON_SIZE_LARGE_TOOLBAR) + + play_img_2 = gtk.Image() + play_img_2.set_from_icon_name('media-playback-start', + gtk.ICON_SIZE_LARGE_TOOLBAR) + pause_img_2 = gtk.Image() + pause_img_2.set_from_icon_name('media-playback-pause', + gtk.ICON_SIZE_LARGE_TOOLBAR) + + paly_1 = ToggleToolButton('media-playback-start-back') + play_2 = ToggleToolButton('media-playback-start') + + paly_1.connect('toggled', self._play_cb, + (paly_1, play_2), (play_img_1, pause_img_1), + self.montage.playbackwards) + self.insert(paly_1, -1) + paly_1.set_tooltip(_('Play backward / Pause')) + + play_2.connect('toggled', self._play_cb, + (play_2, paly_1), (play_img_2, pause_img_2), + self.montage.playforwards) + self.insert(play_2, -1) + play_2.set_tooltip(_('Play forward / Pause')) + + # tempo button + + tempo = TempoSlider(0, 99) + tempo.adjustment.connect("value-changed", self._tempo_cb) + tempo.set_size_request(250, -1) + tempo.set_value(50) + tempo_item = gtk.ToolItem() + tempo_item.add(tempo) + self.insert(tempo_item, -1) + + separator = gtk.SeparatorToolItem() + self.insert(separator,-1) + + # export buttons + + exportframe = ToolButton('image') + exportframe.connect('clicked', self._exportframe_cb) + exportframe.set_tooltip(_('Snapshot')) + self.insert(exportframe, -1) + + self.show_all() + + def _exportframe_cb(self, widget): + self.montage.exportframe() + + def _setframe_cb(self, widget): + self.montage.setframe() + + def _clearframe_cb(self, widget): + self.montage.clearframe() + + def _resetframe_cb(self, widget): + self.montage.reset() + + def _tempo_cb(self, widget): + self.montage.setplayspeed(widget.value) + + def _play_cb(self, widget, buttons, images, play): + if widget.get_active(): + buttons[1].set_active(False) + images[1].show() + widget.set_icon_widget(images[1]) + play() + else: + images[0].show() + widget.set_icon_widget(images[0]) + self.montage.stop() + +class LessonsToolbar(gtk.Toolbar): + def __init__(self): + gtk.Toolbar.__init__(self) + self._mask = False + + for lesson in lessons.THEMES: + button = gtk.ToggleToolButton() + button.set_label(lesson.name) + button.connect('clicked', self._lessons_cb, lesson) + self.insert(button, -1) + + self.get_nth_item(0).set_active(True) + self.show_all() + + def _lessons_cb(self, widget, lesson): + if self._mask: + return + self._mask = True + + for i, j in enumerate(lessons.THEMES): + if j != lesson: + self.get_nth_item(i).set_active(False) + + widget.props.active = True + lesson.change() + self._mask = False diff --git a/activity/activity.info b/activity/activity.info index 70305b6..6e65bb4 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,7 +1,7 @@ [Activity] name = FlipSticks service_name = org.worldwideworkshop.olpc.FlipSticks -class = flipsticks.flipsticksActivity +class = activity.flipsticksActivity icon = activity-flipsticks activity_version = 1 show_launcher = yes diff --git a/animatefp.gif b/animatefp.gif deleted file mode 100644 index 2f85a83..0000000 --- a/animatefp.gif +++ /dev/null Binary files differ diff --git a/export.notes.txt b/export.notes.txt deleted file mode 100644 index 68eea0e..0000000 --- a/export.notes.txt +++ /dev/null @@ -1,2 +0,0 @@ -png2yuv -I p -f 25 -j /tmp/*.png >my.yuv -ffmpeg2theora --optimize my.yuv diff --git a/flipsticks.py b/flipsticks.py deleted file mode 100644 index 3777e87..0000000 --- a/flipsticks.py +++ /dev/null @@ -1,1886 +0,0 @@ -#!/usr/bin/env python - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# - - -### flipsticks -### -### author: Ed Stoner (ed@whsd.net) -### (c) 2007 World Wide Workshop Foundation - -import time -import pygtk -pygtk.require('2.0') -import gtk -import gobject -import os -import math -import textwrap -import pickle - -SERVICE = 'org.freedesktop.Telepathy.Tube.Connect' -IFACE = SERVICE -PATH = '/org/freedesktop/Telepathy/Tube/Connect' -GRAY = "#B7B7B7" # gray -PINK = "#FF0099" # pink -YELLOW = "#FFFF00" # yellow -WHITE = "#FFFFFF" -BLACK = "#000000" -BACKGROUND = "#66CC00" # light green -BUTTON_FOREGROUND = "#CCFB99" # very light green -BUTTON_BACKGROUND = "#027F01" # dark green -COLOR_FG_BUTTONS = ( - (gtk.STATE_NORMAL,"#CCFF99"), - (gtk.STATE_ACTIVE,"#CCFF99"), - (gtk.STATE_PRELIGHT,"#CCFF99"), - (gtk.STATE_SELECTED,"#CCFF99"), - (gtk.STATE_INSENSITIVE,"#CCFF99"), - ) # very light green -COLOR_BG_BUTTONS = ( - (gtk.STATE_NORMAL,"#027F01"), - (gtk.STATE_ACTIVE,"#CCFF99"), - (gtk.STATE_PRELIGHT,"#016D01"), - (gtk.STATE_SELECTED,"#CCFF99"), - (gtk.STATE_INSENSITIVE,"#027F01"), - ) -OLD_COLOR_BG_BUTTONS = ( - (gtk.STATE_NORMAL,"#027F01"), - (gtk.STATE_ACTIVE,"#014D01"), - (gtk.STATE_PRELIGHT,"#016D01"), - (gtk.STATE_SELECTED,"#027F01"), - (gtk.STATE_INSENSITIVE,"#027F01"), - ) - -SPANISH = u'Espa\xf1ol' -LANG = {'English':{'size':'Size', - 'angle':'Angle', - 'lessonplan':'Lesson Plans', - 'lpdir':'lp-en', - 'export':'Export Frame One', - 'HEAD':'Head', - 'NECK':'Neck', - 'RIGHT SHOULDER':'Right Shoulder', - 'UPPER RIGHT ARM':'Upper Right Arm', - 'LOWER RIGHT ARM':'Lower Right Arm', - 'RIGHT HAND':'Right Hand', - 'LEFT SHOULDER':'Left Shoulder', - 'UPPER LEFT ARM':'Upper Left Arm', - 'LOWER LEFT ARM':'Lower Left Arm', - 'LEFT HAND':'Left Hand', - 'TORSO':'Torso', - 'RIGHT HIP':'Right Hip', - 'UPPER RIGHT LEG':'Upper Right Leg', - 'LOWER RIGHT LEG':'Lower Right Leg', - 'RIGHT FOOT':'Right Foot', - 'LEFT HIP':'Left Hip', - 'UPPER LEFT LEG':'Upper Left Leg', - 'LOWER LEFT LEG':'Lower Left Leg', - 'LEFT FOOT':'Left Foot'}, - SPANISH:{'size':u'Tama\xf1o', - 'angle':u'\xe1ngulo', - 'lessonplan':u'Planes de la lecci\xf3n', - 'lpdir':'lp-en', - 'export':'Un marco de la exportacion', - 'HEAD':'Cabeza', - 'NECK':'Cuello', - 'RIGHT SHOULDER':'Hombro derecho', - 'UPPER RIGHT ARM':'Brazo derecho superior', - 'LOWER RIGHT ARM':'Bajar el brazo derecho', - 'RIGHT HAND':'Mano derecha', - 'LEFT SHOULDER':'Hombro izquierdo', - 'UPPER LEFT ARM':'Brazo izquierdo superior', - 'LOWER LEFT ARM':u'Un brazo izquierdo m\xe1s bajo', - 'LEFT HAND':'Mano izquierda', - 'TORSO':'Torso', - 'RIGHT HIP':'Cadera derecha', - 'UPPER RIGHT LEG':'Pierna derecha superior', - 'LOWER RIGHT LEG':'Bajar la pierna derecha', - 'RIGHT FOOT':'Pie derecho', - 'LEFT HIP':'Cadera izquierda', - 'UPPER LEFT LEG':'Pierna izquierda superior', - 'LOWER LEFT LEG':u'Una pierna izquierda m\xe1s baja', - 'LEFT FOOT':'Pie izquierdo'}} - -FPWIDTH = 150 -FPHEIGHT = 100 -#DRAWHEIGHT = 300 for my laptop -KEYFRAMEWIDTH = gtk.gdk.screen_width() - 406 # 675 -KEYFRAMEHEIGHT = 80 -DRAWWIDTH = KEYFRAMEWIDTH + 64 # 750 -DRAWHEIGHT = gtk.gdk.screen_height() - 370 # 500 - -KEYFRAMES = [] # [50,190,337,487,625] -TOTALFRAMES = 30 - -for i in range(5): - keyframe_width = KEYFRAMEWIDTH/5 - KEYFRAMES.append(keyframe_width/2 + i*keyframe_width) - -STICKS = {'HEAD':(0,15), - 'NECK':(90,15), - 'RIGHT SHOULDER':(185,25), - 'UPPER RIGHT ARM':(60,35), - 'LOWER RIGHT ARM':(35,35), - 'LEFT SHOULDER':(355,25), - 'UPPER LEFT ARM':(300,35), - 'LOWER LEFT ARM':(325,35), - 'TORSO':(270,60), - 'RIGHT HIP':(80,20), - 'UPPER RIGHT LEG':(300,50), - 'LOWER RIGHT LEG':(340,40), - 'RIGHT FOOT':(85,15), - 'LEFT HIP':(280,20), - 'UPPER LEFT LEG':(65,50), - 'LOWER LEFT LEG':(15,40), - 'LEFT FOOT':(275,15)} - -PARTS = {'RIGHT HAND':14, - 'LEFT HAND':14} - - -STICKLIST = ['NECK','HEAD','RIGHT SHOULDER','UPPER RIGHT ARM','LOWER RIGHT ARM', - 'LEFT SHOULDER','UPPER LEFT ARM','LOWER LEFT ARM','TORSO', - 'RIGHT HIP','UPPER RIGHT LEG','LOWER RIGHT LEG','RIGHT FOOT', - 'LEFT HIP','UPPER LEFT LEG','LOWER LEFT LEG','LEFT FOOT'] - -LABELLIST = ['HEAD','NECK','RIGHT SHOULDER','UPPER RIGHT ARM','LOWER RIGHT ARM', - 'RIGHT HAND','LEFT SHOULDER','UPPER LEFT ARM','LOWER LEFT ARM','LEFT HAND', - 'TORSO','RIGHT HIP','UPPER RIGHT LEG','LOWER RIGHT LEG','RIGHT FOOT', - 'LEFT HIP','UPPER LEFT LEG','LOWER LEFT LEG','LEFT FOOT'] - -# The joint is the circle at the end of the stick - -JOINTS = {'HEAD':'head', - 'NECK':'neck', - 'RIGHT SHOULDER':'rightshoulder', - 'UPPER RIGHT ARM':'rightelbow', - 'LOWER RIGHT ARM':'righthand', - 'LEFT SHOULDER':'leftshoulder', - 'UPPER LEFT ARM':'leftelbow', - 'LOWER LEFT ARM':'lefthand', - 'TORSO':'groin', - 'RIGHT HIP':'righthip', - 'UPPER RIGHT LEG':'rightknee', - 'LOWER RIGHT LEG':'rightheel', - 'RIGHT FOOT':'righttoe', - 'LEFT HIP':'lefthip', - 'UPPER LEFT LEG':'leftknee', - 'LOWER LEFT LEG':'leftheel', - 'LEFT FOOT':'lefttoe'} - -JOINTTOSTICK = {} -for jname in JOINTS: - JOINTTOSTICK[JOINTS[jname]] = jname - -PARTS = {'HEAD':40, - 'RIGHT HAND':14, - 'LEFT HAND':14} - -TESTSTICKS = {'RIGHT SHOULDER':(37,20), - 'UPPER RIGHT ARM':(6,15), - 'LOWER RIGHT ARM':(10,15)} - -def getwrappedfile(filepath,linelength): - text = [] - f = file(filepath) - for line in f: - if line == '\n': - text.append(line) - else: - for wline in textwrap.wrap(line.strip()): - text.append('%s\n' % wline) - return ''.join(text) - -def capwords(s): - x = s.split() - n = [] - for word in x: - n.append(word.capitalize()) - return ' '.join(n) - -def prepare_btn(btn, w=-1, h=-1): - for state, color in COLOR_BG_BUTTONS: - btn.modify_bg(state, gtk.gdk.color_parse(color)) - c = btn.get_child() - if c is not None: - for state, color in COLOR_FG_BUTTONS: - c.modify_fg(state, gtk.gdk.color_parse(color)) - else: - for state, color in COLOR_FG_BUTTONS: - btn.modify_fg(state, gtk.gdk.color_parse(color)) - if w>0 or h>0: - btn.set_size_request(w, h) - return btn - -def inarea(x,y,awidth,aheight): - if x+5 > awidth: - return False - if y+5 > aheight: - return False - if y < 5: - return False - if x < 5: - return False - return True - -def interpolate(x,x0,y0,x1,y1): - if x1-x0 == 0: - return y0 - m = float(y1-y0)/float(x1-x0) - y = y0 + ((x-x0)*m) - return y - -def getpoints(x,y,angle,len): - nx = int(round(x + (len * math.cos(math.radians(angle))))) - ny = int(round(y - (len * math.sin(math.radians(angle))))) - return (nx,ny) - -def scalesticks(stickdict,i): - for key in stickdict: - (angle,len) = stickdict[key] - newlen = int(len * i) - stickdict[key] = (angle,newlen) - -class flipsticks: - def delete_event(self, widget, event, data=None): - return False - - def destroy(self, widget, data=None): - gtk.main_quit() - - def expose_event(self, widget, event): - x , y, width, height = event.area - widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], - self.pixmap, x, y, x, y, width, height) - return False - - def kf_expose_event(self, widget, event): - x , y, width, height = event.area - widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], - self.kfpixmap, x, y, x, y, width, height) - return False - - def fp_expose_event(self, widget, event): - x , y, width, height = event.area - widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], - self.fppixmap, x, y, x, y, width, height) - return False - - def configure_event(self, widget, event): - x, y, width, height = self.mfdraw.get_allocation() - self.pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) - self.drawmainframe() - return True - - def kf_configure_event(self, widget, event): - self.drawkeyframe() - return True - - def fp_configure_event(self, widget, event): - self.drawfp() - return True - - def motion_notify_event(self, widget, event): - if event.is_hint: - x, y, state = event.window.get_pointer() - else: - x = event.x - y = event.y - state = event.state - if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: - if self.jointpressed: - if inarea(x,y,DRAWWIDTH,DRAWHEIGHT): - #self.joints[self.jointpressed] = (x,y) # old hack way - # first find the parents x,y - (px,py) = self.getparentjoint(self.jointpressed,self.joints, - self.middle) - if x-px == 0: - #computeangle = 0 - b = 1 - else: - b = float(px-x) - a = float(y-py) - computeangle = int(math.degrees(math.atan(a/b))) - stickname = JOINTTOSTICK[self.jointpressed] - # add sum of parent angles to new angle - parents = self.getparentsticks(stickname) - panglesum = 0 - for parentname in parents: - (pangle,plen) = self.sticks[parentname] - panglesum += pangle - (angle, len) = self.sticks[stickname] - #print 'X:%s,Y:%s,PX:%s,PY:%s,ANGLE:%s,NEWANGLE:%s' % (x,y,px,py,angle,newangle) - newangle = computeangle-panglesum - if (x < px) or (b == 1): - newangle = newangle + 180 - if newangle < 0: - newangle = 360 + newangle - self.sticks[stickname] = (newangle,len) - self.setjoints() # this is overkill - self.drawmainframe() - self.updateentrybox() - elif self.middlepressed: - if inarea(x,y,DRAWWIDTH,DRAWHEIGHT): - xdiff = x-self.middle[0] - ydiff = y-self.middle[1] - self.shiftjoints(xdiff,ydiff) - self.middle = (x,y) - self.drawmainframe() - elif self.rotatepressed: - if inarea(x,y,DRAWWIDTH,DRAWHEIGHT): - (px,py) = self.middle - if x-px == 0: - #computeangle = 0 - b = 1 - else: - b = float(px-x) - a = float(y-py) - computeangle = int(math.degrees(math.atan(a/b))) - stickname = 'TORSO' - (angle, len) = self.sticks[stickname] - newangle = computeangle - if (x < px) or (b == 1): - newangle = newangle + 180 - if newangle < 0: - newangle = 360 + newangle - anglediff = newangle-angle - self.sticks[stickname] = (newangle,len) - # now rotate the other sticks off of the middle - for stickname in ['NECK','RIGHT SHOULDER','LEFT SHOULDER']: - (sangle,slen) = self.sticks[stickname] - newsangle = sangle+anglediff - if newsangle < 0: - newsangle = 360 + newsangle - if newsangle > 360: - newsangle = newsangle - 360 - self.sticks[stickname] = (newsangle,slen) - self.setjoints() - self.drawmainframe() - self.updateentrybox() - - return True - - def kf_motion_notify_event(self, widget, event): - if event.is_hint: - x, y, state = event.window.get_pointer() - else: - x = event.x - y = event.y - state = event.state - if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: - if self.kfpressed >= 0: - if inarea(x,y,KEYFRAMEWIDTH,KEYFRAMEHEIGHT): - xdiff = x-self.keyframes[self.kfpressed] - self.shiftjoints(xdiff,0,self.kfsjoints[self.kfpressed]) - self.keyframes[self.kfpressed] = x - self.drawkeyframe() - return True - - def button_press_event(self, widget, event): - if event.button == 1 and self.pixmap != None: - joint = self.injoint(event.x, event.y) - if joint: - self.jointpressed = joint - self.drawmainframe() - elif self.inmiddle(event.x, event.y): - self.middlepressed = True - self.drawmainframe() - elif self.inrotate(event.x, event.y): - self.rotatepressed = True - self.drawmainframe() - return True - - def syncmaintokf(self): - # set the main window to the keyframe - if self.kfsticks[self.kfselected]: - self.sticks = self.kfsticks[self.kfselected].copy() - self.parts = self.kfparts[self.kfselected].copy() - self.middle = self.kfmiddles[self.kfselected] - self.setjoints() - self.drawmainframe() - - def kf_button_press_event(self, widget, event): - if event.button == 1 and self.pixmap != None: - kfnum = self.inkeyframe(event.x, event.y) - if kfnum >= 0: - self.kfpressed = kfnum - self.kfselected = kfnum - self.drawkeyframe() - self.syncmaintokf() - self.updateentrybox() - return True - - def button_release_event(self, widget, event): - self.jointpressed = None - self.middlepressed = False - self.rotatepressed = False - self.drawmainframe() - return True - - def kf_button_release_event(self, widget, event): - self.kfpressed = -1 - self.drawkeyframe() - return True - - def setplayspeed(self,adj): - #self.waittime = int((6-adj.value)*150) - self.waittime = int((6-adj.value)*75) - if self.playing: - gobject.source_remove(self.playing) - self.playing = gobject.timeout_add(self.waittime, self.playframe) - - def playframe(self): - if not self.playing: - return False - else: - if self.playframenum == -1: - return True - joints = self.frames[self.playframenum] - parts = self.fparts[self.playframenum] - # draw on the main drawing area - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 3 - cm = drawgc.get_colormap() - white = cm.alloc_color('white') - black = cm.alloc_color('black') - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.mfdraw.get_allocation() - #self.pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) - # clear area - drawgc.set_foreground(white) - self.pixmap.draw_rectangle(drawgc,True,0,0,width,height) - - drawgc.set_foreground(black) - #hsize = self.sticks['HEAD'][1] # really half of head size - hsize = self.fhsize[self.playframenum] - middle = self.fmiddles[self.playframenum] - rhsize = parts['RIGHT HAND'] - lhsize = parts['LEFT HAND'] - self.drawstickman(drawgc,self.pixmap,middle,joints,hsize,rhsize,lhsize) - # draw circle for middle - #green = cm.alloc_color('green') - #drawgc.set_foreground(green) - #x,y = middle - #self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - self.mfdraw.queue_draw() - - fsecs = self.frames.keys() - fsecs.sort() - if self.playingbackwards: - # decrement playframenum - if self.playframenum == fsecs[0]: - self.playframenum = fsecs[-1] - else: - i = fsecs.index(self.playframenum) - self.playframenum = fsecs[i-1] - else: - # increment playframenum - if self.playframenum == fsecs[-1]: - self.playframenum = fsecs[0] - else: - i = fsecs.index(self.playframenum) - self.playframenum = fsecs[i+1] - if self.playing: - return True - else: - return False - - def enterangle_callback(self, widget, entry): - stickname = self.stickselected - if stickname in self.sticks: - newangle = int(entry.get_text()) - (angle, len) = self.sticks[stickname] - self.sticks[stickname] = (newangle,len) - else: - # part not stick - self.angleentry.set_text('-') - self.setjoints() - self.drawmainframe() - - def updateentrybox(self): - if self.stickselected in self.sticks: - (angle, len) = self.sticks[self.stickselected] - self.angleentry.set_text(str(angle)) - else: - # part not stick - len = self.parts[self.stickselected] - self.sizeentry.set_text(str(len)) - - def enterlen_callback(self, widget, entry): - stickname = self.stickselected - newlen = int(entry.get_text()) - if stickname in self.sticks: - if stickname == 'HEAD': - newlen = int(newlen/2.0) - (angle, len) = self.sticks[stickname] - self.sticks[stickname] = (angle,newlen) - else: - # part not stick - self.parts[stickname] = newlen - self.setjoints() - self.drawmainframe() - - def reset(self, widget, data=None): - xmiddle = int(DRAWWIDTH/2.0) - ymiddle = int(DRAWHEIGHT/2.0) - self.middle = (xmiddle,ymiddle) - self.sticks = STICKS.copy() - self.parts = PARTS.copy() - self.selectstickebox() - self.setjoints() - self.drawmainframe() - - def setframe(self, widget, data=None): - self.kfmiddles[self.kfselected] = self.middle - self.kfparts[self.kfselected] = self.parts.copy() - self.kfsticks[self.kfselected] = self.sticks.copy() - self.kfssticks[self.kfselected] = self.sticks.copy() - scalesticks(self.kfssticks[self.kfselected],.2) - self.kfjoints[self.kfselected] = self.joints.copy() - self.kfsjoints[self.kfselected] = self.initjoints() - #x, y, width, height = self.kfdraw.get_allocation() - #y = int(height/2.0) - #y = int(KEYFRAMEHEIGHT/2.0)-5 - y = int(KEYFRAMEHEIGHT/2.0) - x = self.keyframes[self.kfselected] - kfmiddle = (x,y) - self.setjoints(self.kfsjoints[self.kfselected],self.kfssticks[self.kfselected],kfmiddle) - self.drawkeyframe() - - def clearframe(self, widget, data=None): - self.kfsticks[self.kfselected] = None - self.kfssticks[self.kfselected] = None - self.kfjoints[self.kfselected] = None - self.kfsjoints[self.kfselected] = None - self.kfparts[self.kfselected] = None - self.drawkeyframe() - - def intjoints(self,sjoints,ejoints,count,numpoints): - # numpoints: number of points between start and end - # count: point were getting now - ijoints = {} - for jname in sjoints: - (x0,y0) = sjoints[jname] - (x1,y1) = ejoints[jname] - #print 'x0:%s,y0:%s' % (x0,y0) - #print 'x1:%s,y1:%s' % (x1,y1) - x = x0 + (count * ((x1-x0)/float(numpoints))) - y = interpolate(x,x0,y0,x1,y1) - ijoints[jname] = (int(x),int(y)) - return ijoints - - def intparts(self,sparts,eparts,count,numpoints): - iparts = {} - for pname in sparts: - x0 = sparts[pname] - x1 = eparts[pname] - if x0 == x1: - iparts[pname] = x0 - continue - x = x0 + (count * ((x1-x0)/float(numpoints))) - iparts[pname] = int(x) - return iparts - - def inthsize(self,shsize,ehsize,count,numpoints): - x0 = shsize - x1 = ehsize - if x0 == x1: - return x0 - x = x0 + (count * ((x1-x0)/float(numpoints))) - return int(x) - - def intmiddle(self,smiddle,emiddle,count,numpoints): - (x0,y0) = smiddle - (x1,y1) = emiddle - x = x0 + (count * ((x1-x0)/float(numpoints))) - y = interpolate(x,x0,y0,x1,y1) - return (int(x),int(y)) - - def makeframes(self): - endsecs = KEYFRAMEWIDTH - fint = int(endsecs/float(TOTALFRAMES)) # frame interval - self.frames = {} - self.fparts = {} - self.fmiddles = {} - self.fhsize = {} - kf = {} # point to keyframes by x-middle (which represents a time, like seconds) - for i in range(len(self.keyframes)): - secs = self.keyframes[i] - #kf[secs] = i - # use self.kfjoints[kf[secs]] and self.kfparts[kf[secs]] - if self.kfjoints[i]: - self.frames[secs] = self.kfjoints[i].copy() - self.fparts[secs] = self.kfparts[i].copy() - self.fmiddles[secs] = self.kfmiddles[i] - #print '%s:KFMIDDLE:%s = (%s,%s)' % (i,secs,self.fmiddles[secs][0],self.fmiddles[secs][1]) - self.fhsize[secs] = self.kfsticks[i]['HEAD'][1] - fsecs = self.frames.keys() - fsecs.sort() - if not fsecs: - return - # ADD frame at 0 - self.frames[0] = self.frames[fsecs[0]].copy() - self.fparts[0] = self.fparts[fsecs[0]].copy() - self.fmiddles[0] = self.fmiddles[fsecs[0]] - self.fhsize[0] = self.fhsize[fsecs[0]] - # ADD frame at end - self.frames[endsecs] = self.frames[fsecs[-1]].copy() - self.fparts[endsecs] = self.fparts[fsecs[-1]].copy() - self.fmiddles[endsecs] = self.fmiddles[fsecs[-1]] - self.fhsize[endsecs] = self.fhsize[fsecs[-1]] - # now fill in frames between - fsecs = self.frames.keys() - fsecs.sort() - for i in range(len(fsecs)): - if i == len(fsecs)-1: - continue # nothing after end - startsecs = fsecs[i] - endsecs = fsecs[i+1] - numframes = int((endsecs-startsecs)/float(fint))-1 - #print 'NUMFRAMES(%s):%s' % (i,numframes) - for j in range(numframes-1): # MAYBE SHOULD BE numframes - secs = startsecs + ((j+1)*fint) - self.frames[secs] = self.intjoints(self.frames[startsecs],self.frames[endsecs], - j+1,numframes) - self.fparts[secs] = self.intparts(self.fparts[startsecs],self.fparts[endsecs], - j+1,numframes) - self.fmiddles[secs] = self.intmiddle(self.fmiddles[startsecs],self.fmiddles[endsecs], - j+1,numframes) - self.fhsize[secs] = self.inthsize(self.fhsize[startsecs],self.fhsize[endsecs], - j+1,numframes) - #print '%s,%s(%s secs):(%s,%s) START(%s,%s) - END(%s,%s) startsecs:%s endsecs:%s numframes:%s' % (i,j,secs,self.fmiddles[secs][0],self.fmiddles[secs][1],self.fmiddles[startsecs][0],self.fmiddles[startsecs][1],self.fmiddles[endsecs][0],self.fmiddles[endsecs][1],startsecs,endsecs,numframes) - #print self.frames.keys() - - def shiftjoints(self,xdiff,ydiff,joints=None): - if not joints: - joints = self.joints - for jname in joints: - #if isinstance(self.joints[jname],tuple): - (jx,jy) = joints[jname] - njx = jx + xdiff - njy = jy + ydiff - joints[jname] = (njx,njy) - - def initjoints(self): - joints = {} - for stickname in JOINTS: - jname = JOINTS[stickname] - joints[jname] = (0,0) - return joints - - def getparentsticks(self, stickname): - if stickname in ['RIGHT SHOULDER','LEFT SHOULDER','NECK','TORSO']: - return [] - if stickname in ['HEAD']: - return ['NECK'] - if stickname == 'UPPER RIGHT ARM': - return ['RIGHT SHOULDER'] - if stickname == 'LOWER RIGHT ARM': - return ['UPPER RIGHT ARM','RIGHT SHOULDER'] - if stickname == 'UPPER LEFT ARM': - return ['LEFT SHOULDER'] - if stickname == 'LOWER LEFT ARM': - return ['UPPER LEFT ARM','LEFT SHOULDER'] - if stickname == 'RIGHT HIP': - return ['TORSO'] - if stickname == 'UPPER RIGHT LEG': - return ['RIGHT HIP','TORSO'] - if stickname == 'LOWER RIGHT LEG': - return ['UPPER RIGHT LEG','RIGHT HIP','TORSO'] - if stickname == 'RIGHT FOOT': - return ['LOWER RIGHT LEG','UPPER RIGHT LEG','RIGHT HIP','TORSO'] - if stickname == 'LEFT HIP': - return ['TORSO'] - if stickname == 'UPPER LEFT LEG': - return ['LEFT HIP','TORSO'] - if stickname == 'LOWER LEFT LEG': - return ['UPPER LEFT LEG','LEFT HIP','TORSO'] - if stickname == 'LEFT FOOT': - return ['LOWER LEFT LEG','UPPER LEFT LEG','LEFT HIP','TORSO'] - - def getparentjoint(self,jname,joints,middle): - if jname in ['rightshoulder','leftshoulder','groin','neck']: - return middle - parentjoints = {'rightelbow':'rightshoulder', - 'righthand':'rightelbow', - 'leftelbow':'leftshoulder', - 'lefthand':'leftelbow', - 'righthip':'groin', - 'rightknee':'righthip', - 'rightheel':'rightknee', - 'righttoe':'rightheel', - 'lefthip':'groin', - 'leftknee':'lefthip', - 'leftheel':'leftknee', - 'lefttoe':'leftheel', - 'head':'neck'} - return joints[parentjoints[jname]] - - def setjoints(self,joints=None,sticks=None,middle=None): - if not joints: - joints = self.joints - if not sticks: - sticks = self.sticks - if not middle: - middle = self.middle - # have to traverse in order because - # parent joints must be set right - for stickname in self.sticklist: - (angle,len) = sticks[stickname] - jname = JOINTS[stickname] - (x,y) = self.getparentjoint(jname,joints,middle) - parents = self.getparentsticks(stickname) - panglesum = 0 - for parentname in parents: - (pangle,plen) = sticks[parentname] - panglesum += pangle - (nx,ny) = getpoints(x,y,angle+panglesum,len) - joints[jname] = (nx,ny) - - def drawmainframe(self): - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 3 - cm = drawgc.get_colormap() - red = cm.alloc_color('red') - yellow = cm.alloc_color('yellow') - white = cm.alloc_color('white') - black = cm.alloc_color('black') - blue = cm.alloc_color('blue') - green = cm.alloc_color('green') - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.mfdraw.get_allocation() - #self.pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) - # clear area - drawgc.set_foreground(white) - self.pixmap.draw_rectangle(drawgc,True,0,0,width,height) - - drawgc.set_foreground(black) - hsize = self.sticks['HEAD'][1] # really half of head size - rhsize = self.parts['RIGHT HAND'] - lhsize = self.parts['LEFT HAND'] - self.drawstickman(drawgc,self.pixmap,self.middle,self.joints,hsize,rhsize,lhsize) - # draw circle for middle - drawgc.set_foreground(green) - if self.middlepressed: - drawgc.set_foreground(blue) - x,y = self.middle - self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - # draw circle for rotate (should be halfway between middle and groin - (rx,ry) = self.getrotatepoint() - drawgc.set_foreground(yellow) - if self.rotatepressed: - drawgc.set_foreground(blue) - self.pixmap.draw_arc(drawgc,True,rx-5,ry-5,10,10,0,360*64) - # draw circles for joints - drawgc.set_foreground(black) - for jname in self.joints: - if jname == 'head': - continue - x,y = self.joints[jname] - if self.jointpressed == jname: - drawgc.set_foreground(blue) - self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - drawgc.set_foreground(black) - else: - drawgc.set_foreground(red) - self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - drawgc.set_foreground(black) - self.mfdraw.queue_draw() - - def drawstickman(self,drawgc,pixmap,middle,joints,hsize,rhsize,lhsize): - leftarm = [middle, joints['leftshoulder'],joints['leftelbow'],joints['lefthand']] - rightarm = [middle, joints['rightshoulder'],joints['rightelbow'],joints['righthand']] - torso = [joints['neck'],middle,joints['groin']] - leftleg = [joints['groin'],joints['lefthip'],joints['leftknee'], - joints['leftheel'],joints['lefttoe']] - rightleg = [joints['groin'],joints['righthip'],joints['rightknee'], - joints['rightheel'],joints['righttoe']] - # draw lines - pixmap.draw_lines(drawgc, leftarm) - pixmap.draw_lines(drawgc, rightarm) - pixmap.draw_lines(drawgc, torso) - pixmap.draw_lines(drawgc, leftleg) - pixmap.draw_lines(drawgc, rightleg) - # draw head - x,y = joints['head'] - pixmap.draw_arc(drawgc,True,x-hsize,y-hsize,hsize*2,hsize*2,0,360*64) - # draw circles for hands - x,y = joints['righthand'] - pixmap.draw_arc(drawgc,True,x-int(rhsize/2.0),y-int(rhsize/2.0),rhsize,rhsize,0,360*64) - x,y = joints['lefthand'] - pixmap.draw_arc(drawgc,True,x-int(lhsize/2.0),y-int(lhsize/2.0),lhsize,lhsize,0,360*64) - - def drawkeyframe(self): - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 2 - cm = drawgc.get_colormap() - red = cm.alloc_color('red') - white = cm.alloc_color('white') - black = cm.alloc_color('black') - blue = cm.alloc_color('blue') - green = cm.alloc_color('green') - pink = cm.alloc_color(PINK) - bgcolor = cm.alloc_color(BACKGROUND) - darkgreen = cm.alloc_color(BUTTON_BACKGROUND) - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.kfdraw.get_allocation() - self.kfpixmap = gtk.gdk.Pixmap(self.kfdraw.window, width, height) - # clear area - drawgc.set_foreground(bgcolor) - self.kfpixmap.draw_rectangle(drawgc,True,0,0,width,height) - # draw line in middle - drawgc.set_foreground(darkgreen) - self.kfpixmap.draw_rectangle(drawgc,True,10,int(height/2.0)-5,width-20,10) - x = 10 - y = int(height/2.0) - self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - x = width-10 - self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - # draw the keyframe circles - for i in range(len(self.keyframes)): - # first the outer circle - x = self.keyframes[i] - if i == self.kfselected: - drawgc.set_foreground(pink) - else: - drawgc.set_foreground(darkgreen) - self.kfpixmap.draw_arc(drawgc,True,x-40,y-40,80,80,0,360*64) - # then the inner circle - drawgc.set_foreground(white) - self.kfpixmap.draw_arc(drawgc,True,x-35,y-35,70,70,0,360*64) - if self.kfssticks[i]: - # draw a man in the circle - drawgc.set_foreground(black) - hsize = self.kfssticks[i]['HEAD'][1] - rhsize = int(self.kfparts[i]['RIGHT HAND']*0.2) - lhsize = int(self.kfparts[i]['LEFT HAND']*0.2) - self.drawstickman(drawgc,self.kfpixmap,(x,y),self.kfsjoints[i],hsize,rhsize,lhsize) - #self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) - self.kfdraw.queue_draw() - - def drawfp(self): - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 1 - cm = drawgc.get_colormap() - red = cm.alloc_color('red') - white = cm.alloc_color('white') - black = cm.alloc_color('black') - blue = cm.alloc_color('blue') - green = cm.alloc_color('green') - pink = cm.alloc_color(PINK) - bgcolor = cm.alloc_color(BACKGROUND) - darkgreen = cm.alloc_color(BUTTON_BACKGROUND) - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.fpdraw.get_allocation() - self.fppixmap = gtk.gdk.Pixmap(self.fpdraw.window, width, height) - # clear area - drawgc.set_foreground(white) - self.fppixmap.draw_rectangle(drawgc,True,0,0,width,height) - self.fpdraw.queue_draw() - - def inkeyframe(self, x, y): - for i in range(len(self.keyframes)): - kx = self.keyframes[i] - if (abs(kx-x) <= 20): - return i - return -1 - - def injoint(self, x, y): - for jname in self.joints: - jx, jy = self.joints[jname] - if (abs(jx-x) <= 5) and (abs(jy-y) <= 5): - return jname - - def inmiddle(self, x, y): - mx, my = self.middle - if (abs(mx-x) <= 5) and (abs(my-y) <= 5): - return True - - def inrotate(self, x, y): - rx, ry = self.getrotatepoint() - if (abs(rx-x) <= 5) and (abs(ry-y) <= 5): - return True - - def getrotatepoint(self): - (angle,len) = self.sticks['TORSO'] - x,y = self.middle - (rx,ry) = getpoints(x,y,angle,int(len/2.0)) - return (rx,ry) - - def selectstick(self, widget, event, data=None): - if data: - if self.stickselected: - ebox = self.stickbuttons[self.stickselected] - ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) - label = ebox.get_child() - label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_FOREGROUND)) - self.stickselected = data - self.selectstickebox() - - def selectstickebox(self): - ebox = self.stickbuttons[self.stickselected] - ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_FOREGROUND)) - label = ebox.get_child() - label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) - if self.stickselected in self.sticks: - if self.stickselected == 'HEAD': - self.sizeentry.set_text(str(self.sticks[self.stickselected][1]*2)) - else: - self.sizeentry.set_text(str(self.sticks[self.stickselected][1])) - - self.angleentry.set_text(str(self.sticks[self.stickselected][0])) - else: - # its a part not a stick - self.angleentry.set_text('-') - self.sizeentry.set_text(str(self.parts[self.stickselected])) - - def showlessonplans(self, widget, data=None): - dia = gtk.Dialog(title='Lesson Plans', - parent=None, - flags=0, - buttons=None) - dia.set_default_size(500,500) - dia.show() - - #dia.vbox.pack_start(scrolled_window, True, True, 0) - notebook = gtk.Notebook() - # uncomment below to highlight tabs - notebook.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(WHITE)) - notebook.set_tab_pos(gtk.POS_TOP) - #notebook.set_default_size(400,400) - notebook.show() - lessonplans = {} - lpdir = os.path.join(self.mdirpath,LANG[self.language]['lpdir']) - lpentries = os.listdir(lpdir) - for entry in lpentries: - fpath = os.path.join(lpdir,entry) - lessonplans[entry] = getwrappedfile(fpath,95) - lpkeys = lessonplans.keys() - lpkeys.sort() - for lpkey in lpkeys: - lpname = lpkey.replace('_',' ').replace('0','')[:-4] - label = gtk.Label(lessonplans[lpkey]) - #if self.insugar: - # label.modify_fg(gtk.STATE_NORMAL,gtk.gdk.color_parse(WHITE)) - eb = gtk.EventBox() - eb.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(WHITE)) - #label.set_line_wrap(True) - label.show() - eb.add(label) - eb.show() - #tlabel = gtk.Label('Lesson Plan %s' % str(i+1)) - tlabel = gtk.Label(lpname) - tlabel.show() - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) - scrolled_window.show() - scrolled_window.add_with_viewport(eb) - notebook.append_page(scrolled_window, tlabel) - #dia.action_area.pack_start(notebook, True, True, 0) - dia.vbox.pack_start(notebook, True, True, 0) - result = dia.run() - dia.destroy() - - def loadfile(self, widget, data=None): - pass - - def savefile(self, widget, data=None): - pass - - def exportanim(self, widget, data=None): - if self.insugar: - self.exporttojournal() - else: - self.exportfile() - - def exportfile(self): - self.makeframes() - fsecs = self.frames.keys() - tmpdir = '/tmp' - pngpaths = [] - for i in fsecs: - joints = self.frames[i] - parts = self.fparts[i] - # draw on the main drawing area - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 3 - cm = drawgc.get_colormap() - white = cm.alloc_color('white') - black = cm.alloc_color('black') - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.mfdraw.get_allocation() - pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) - # clear area - drawgc.set_foreground(white) - pixmap.draw_rectangle(drawgc,True,0,0,width,height) - - drawgc.set_foreground(black) - #hsize = self.sticks['HEAD'][1] # really half of head size - hsize = self.fhsize[i] - middle = self.fmiddles[i] - rhsize = parts['RIGHT HAND'] - lhsize = parts['LEFT HAND'] - self.drawstickman(drawgc,pixmap,middle,joints,hsize,rhsize,lhsize) - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) - gtk.gdk.Pixbuf.get_from_drawable(pixbuf,pixmap,pixmap.get_colormap(),0,0,0,0,width,height) - filename = 'fp%03d.png' % i - filepath = os.path.join(tmpdir,filename) - pixbuf.save(filepath,'png') - pngpaths.append(filepath) - # now convert all of the pngs to gif and then make a gif animation - convertpath = 'convert' - tmpdir = '/tmp' - entires = os.listdir(tmpdir) - gifpaths = [] - for pngfilepath in pngpaths: - giffilepath = pngfilepath[:-4]+'.gif' - gifpaths.append(giffilepath) - os.system('%s %s %s' % (convertpath,pngfilepath,giffilepath)) - #os.remove(pngfilepath) - agifpath = os.path.join(self.mdirpath,'animatefp.gif') - os.system('%s -delay 20 -loop 0 %s/fp*.gif %s' % (convertpath,tmpdir,agifpath)) - for giffilepath in gifpaths: - os.remove(giffilepath) - - def exporttojournal(self): - self.makeframes() - fsecs = self.frames.keys() - tmpdir = '/tmp' - pngpaths = [] - firstpixindex = fsecs[0] - for i in [fsecs[0]]: - joints = self.frames[i] - parts = self.fparts[i] - # draw on the main drawing area - area = self.toplevel.window - drawgc = area.new_gc() - drawgc.line_width = 3 - cm = drawgc.get_colormap() - white = cm.alloc_color('white') - black = cm.alloc_color('black') - drawgc.fill = gtk.gdk.SOLID - x, y, width, height = self.mfdraw.get_allocation() - pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) - # clear area - drawgc.set_foreground(white) - pixmap.draw_rectangle(drawgc,True,0,0,width,height) - - drawgc.set_foreground(black) - #hsize = self.sticks['HEAD'][1] # really half of head size - hsize = self.fhsize[i] - middle = self.fmiddles[i] - rhsize = parts['RIGHT HAND'] - lhsize = parts['LEFT HAND'] - self.drawstickman(drawgc,pixmap,middle,joints,hsize,rhsize,lhsize) - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) - if i == firstpixindex: - firstpixbuf = pixbuf - gtk.gdk.Pixbuf.get_from_drawable(pixbuf,pixmap,pixmap.get_colormap(),0,0,0,0,width,height) - filename = 'fp%03d.png' % i - filepath = os.path.join(tmpdir,filename) - pixbuf.save(filepath,'png') - pngpaths.append(filepath) - from sugar.datastore import datastore - mediaObject = datastore.create() - mediaObject.metadata['title'] = 'FlipSticks PNG' - thumbData = self._get_base64_pixbuf_data(firstpixbuf) - mediaObject.metadata['preview'] = thumbData - #medaiObject.metadata['icon-color'] = '' - mediaObject.metadata['mime_type'] = 'image/png' - mediaObject.file_path = pngpaths[0] - datastore.write(mediaObject) - - def _get_base64_pixbuf_data(self, pixbuf): - data = [""] - pixbuf.save_to_callback(self._save_data_to_buffer_cb, "png", {}, data) - import base64 - return base64.b64encode(str(data[0])) - - def _save_data_to_buffer_cb(self, buf, data): - data[0] += buf - return True - - def playbackwards(self, widget, data=None): - if self.playing: - playimg = gtk.Image() - playimg.set_from_file(os.path.join(self.iconsdir,'big_left_arrow.png')) - playimg.show() - widget.set_image(playimg) - self.playing = False - # set the main window to the keyframe - if self.kfsticks[self.kfselected]: - self.sticks = self.kfsticks[self.kfselected].copy() - self.parts = self.kfparts[self.kfselected].copy() - self.middle = self.kfmiddles[self.kfselected] - self.setjoints() - self.drawmainframe() - self.updateentrybox() - else: - stopimg = gtk.Image() - stopimg.set_from_file(os.path.join(self.iconsdir,'big_pause.png')) - stopimg.show() - widget.set_image(stopimg) - self.makeframes() - fsecs = self.frames.keys() - fsecs.sort() - if fsecs: - self.playframenum = fsecs[-1] - else: - self.playframenum = -1 - self.playingbackwards = True - self.playing = gobject.timeout_add(self.waittime, self.playframe) - - def playforwards(self, widget, data=None): - if self.playing: - playimg = gtk.Image() - playimg.set_from_file(os.path.join(self.iconsdir,'big_right_arrow.png')) - playimg.show() - widget.set_image(playimg) - self.playing = False - # set the main window to the keyframe - if self.kfsticks[self.kfselected]: - self.sticks = self.kfsticks[self.kfselected].copy() - self.parts = self.kfparts[self.kfselected].copy() - self.middle = self.kfmiddles[self.kfselected] - self.setjoints() - self.drawmainframe() - self.updateentrybox() - else: - stopimg = gtk.Image() - stopimg.set_from_file(os.path.join(self.iconsdir,'big_pause.png')) - stopimg.show() - widget.set_image(stopimg) - self.makeframes() - #mkeys = self.fmiddles.keys() - #mkeys.sort() - #for mkey in mkeys: - # print '%s:(%s,%s)' % (mkey,self.fmiddles[mkey][0],self.fmiddles[mkey][1]) - fsecs = self.frames.keys() - fsecs.sort() - if fsecs: - self.playframenum = fsecs[0] - else: - self.playframenum = -1 - self.playingbackwards = False - self.playing = gobject.timeout_add(self.waittime, self.playframe) - - def changed_cb(self, combobox): - model = combobox.get_model() - index = combobox.get_active() - if index: - lang = model[index][0] - if lang == 'Espa\xc3\xb1ol': - lang = SPANISH - if lang in LANG: - self.lessonplans.set_label(LANG[lang]['lessonplan']) - self.anglelabel.set_label(LANG[lang]['angle']+':') - self.sizelabel.set_label(LANG[lang]['size']+':') - self.export.set_label(LANG[lang]['export']) - for stickpartname in self.labellist: - label = self.sticklabels[stickpartname] - label.set_label(LANG[lang][stickpartname]) - prepare_btn(self.lessonplans) - prepare_btn(self.export) - return - - def setlastlanguage(self, widget, data=None): - li = LANGLIST.index(self.language) - if li == 0: - self.language = LANGLIST[len(LANGLIST)-1] - else: - self.language = LANGLIST[li-1] - self.changebuttonlang() - - def setnextlanguage(self, widget, data=None): - li = LANGLIST.index(self.language) - if li == (len(LANGLIST)-1): - self.language = LANGLIST[0] - else: - self.language = LANGLIST[li+1] - self.changebuttonlang() - - def getdefaultlang(self): - return 'English' - - def getsdata(self): - self.makeframes() - sdd = {} # save data dictionary - sdd['kfmiddles'] = self.kfmiddles - sdd['kfparts'] = self.kfparts - sdd['kfsticks'] = self.kfsticks - sdd['kfssticks'] = self.kfssticks - sdd['kfjoints'] = self.kfjoints - sdd['kfsjoints'] = self.kfsjoints - sdd['keyframes'] = self.keyframes - sdd['kfselected'] = self.kfselected - return pickle.dumps(sdd) - - def restore(self, sdata): - sdd = pickle.loads(sdata) - self.kfmiddles = sdd['kfmiddles'] - self.kfparts = sdd['kfparts'] - self.kfsticks = sdd['kfsticks'] - self.kfssticks = sdd['kfssticks'] - self.kfjoints = sdd['kfjoints'] - self.kfsjoints = sdd['kfsjoints'] - self.keyframes = sdd['keyframes'] - self.kfselected = sdd['kfselected'] - self.drawkeyframe() - self.syncmaintokf() - self.updateentrybox() - - def __init__(self, toplevel_window, mdirpath): - self.insugar = False - self.playing = False - self.playingbackwards = False - self.waittime = 3*150 - self.keyframe = 0 - self.toplevel = toplevel_window - self.mdirpath = mdirpath - xmiddle = int(DRAWWIDTH/2.0) - ymiddle = int(DRAWHEIGHT/2.0) - self.middle = (xmiddle,ymiddle) - self.sticks = STICKS.copy() - self.parts = PARTS.copy() - self.sticklist = STICKLIST - self.labellist = LABELLIST - self.stickselected = 'RIGHT SHOULDER' - self.laststickselected = None - self.keyframes = KEYFRAMES - self.kfsticks = [None,None,None,None,None] - self.kfssticks = [None,None,None,None,None] - self.kfjoints = [None,None,None,None,None] - self.kfsjoints = [None,None,None,None,None] - self.kfmiddles = [None,None,None,None,None] - self.kfparts = [None,None,None,None,None] - self.kfselected = 0 - self.joints = self.initjoints() - self.setjoints() - self.jointpressed = None - self.kfpressed = -1 - self.middlepressed = False - self.rotatepressed = False - self.iconsdir = os.path.join(self.mdirpath,'icons') - self.language = self.getdefaultlang() - - self.mpbox = gtk.VBox() - self.main = gtk.EventBox() - self.main.show() - self.main.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(YELLOW)) - self.mainbox = gtk.EventBox() - self.mainbox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - self.mainbox.set_border_width(5) - self.mainbox.show() - self.main.add(self.mainbox) - self.mpbox.show() - self.hbox = gtk.HBox() - self.hbox.show() - self.vbox = gtk.VBox() - self.vbox.show() - self.hbox.pack_start(self.vbox,False,False,0) - self.mainbox.add(self.mpbox) - self.mpbox.pack_start(self.hbox,True,True,0) - - self.logobox = gtk.HBox(False,0) - self.logobox.show() - self.logo = gtk.Image() - self.logo.show() - self.logo.set_from_file(os.path.join(self.iconsdir,'logo.png')) - self.logobox.pack_start(self.logo,False,False,0) - self.lessonplans = gtk.Button('Lesson Plans') - self.lessonplans.connect('clicked',self.showlessonplans, None) - prepare_btn(self.lessonplans) - self.lessonplans.show() - self.lpvbox = gtk.VBox() - self.lpvbox.show() - self.lpvbox.pack_start(self.lessonplans,True,False,0) - self.lpoframe = gtk.EventBox() - self.lpoframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(YELLOW)) - self.lpoframe.show() - self.lpframe = gtk.EventBox() - self.lpframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - self.lpframe.show() - self.lpalign = gtk.Alignment(1.0,1.0,1.0,1.0) - self.lpalign.add(self.lpframe) - self.lpalign.set_padding(0,5,5,0) - self.lpalign.show() - self.lpoframe.add(self.lpalign) - self.lphbox = gtk.HBox() - self.lphbox.show() - self.lphbox.pack_start(self.lpvbox,True,False,0) - self.lpframe.add(self.lphbox) - self.logobox.pack_start(self.lpoframe,True,True,0) - #vvvvv LANGUAGE BUTTONS vvvvv - #self.lastlang = gtk.Button() - #self.lastlang.connect('clicked', self.setlastlanguage, None) - #llla = gtk.Image() - #llla.set_from_file(os.path.join(self.iconsdir,'left_arrow.png')) - #llla.show() - #self.lastlang.add(llla) - #prepare_btn(self.lastlang) - #self.lastlang.show() - #self.llvbox = gtk.VBox() - #self.llvbox.show() - #self.llvbox.pack_start(self.lastlang,True,False,0) - #self.lang = gtk.Button(self.language) - #prepare_btn(self.lang) - #self.lang.show() - #self.nextlang = gtk.Button() - #self.nextlang.connect('clicked', self.setnextlanguage, None) - #nlra = gtk.Image() - #nlra.set_from_file(os.path.join(self.iconsdir,'right_arrow.png')) - #nlra.show() - #self.nextlang.add(nlra) - #prepare_btn(self.nextlang) - #self.nextlang.show() - #self.nlvbox = gtk.VBox() - #self.nlvbox.show() - #self.nlvbox.pack_start(self.nextlang,True,False,0) - #self.langvbox = gtk.VBox() - #self.langvbox.show() - #self.langvbox.pack_start(self.lang,True,False,0) - #^^^^^ LANGUAGE BUTTONS ^^^^^ - #vvvvv LANGUAGE DROPDOWN vvvvv - self.langdd = gtk.combo_box_new_text() - self.langdd.append_text('Language') - self.langdd.append_text('English') - self.langdd.append_text(SPANISH) - self.langdd.connect('changed', self.changed_cb) - self.langdd.set_active(0) - self.langdd.show() - self.langddvbox = gtk.VBox() - self.langddvbox.show() - self.langddvbox.pack_start(self.langdd,True,False,0) - #^^^^^ LANGUAGE DROPDOWN ^^^^^ - self.langoframe = gtk.EventBox() - self.langoframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(YELLOW)) - self.langoframe.show() - self.langframe = gtk.EventBox() - self.langframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - self.langframe.show() - self.langalign = gtk.Alignment(1.0,1.0,1.0,1.0) - self.langalign.add(self.langframe) - self.langalign.set_padding(0,5,5,5) - self.langalign.show() - self.langoframe.add(self.langalign) - self.langhbox = gtk.HBox() - self.langhbox.show() - #self.langhbox.pack_start(self.llvbox,True,False,0) - #self.langhbox.pack_start(self.langvbox,True,False,0) - #self.langhbox.pack_start(self.nlvbox,True,False,0) - self.langhbox.pack_start(self.langddvbox,True,False,0) - self.langframe.add(self.langhbox) - self.logobox.pack_start(self.langoframe,True,True,0) - - self.vbox.pack_start(self.logobox,False,False,0) - - #self.drawhbox = gtk.HBox() - #self.drawhbox.show() - self.mfdraw = gtk.DrawingArea() - self.mfdraw.set_size_request(DRAWWIDTH,DRAWHEIGHT) - self.mfdraw.show() - self.mfdraw.connect('expose_event', self.expose_event) - self.mfdraw.connect('configure_event', self.configure_event) - self.mfdraw.connect('motion_notify_event', self.motion_notify_event) - self.mfdraw.connect('button_press_event', self.button_press_event) - self.mfdraw.connect('button_release_event', self.button_release_event) - self.mfdraw.set_events(gtk.gdk.EXPOSURE_MASK - | gtk.gdk.LEAVE_NOTIFY_MASK - | gtk.gdk.BUTTON_PRESS_MASK - | gtk.gdk.BUTTON_RELEASE_MASK - | gtk.gdk.POINTER_MOTION_MASK - | gtk.gdk.POINTER_MOTION_HINT_MASK) - self.drawborder = gtk.EventBox() - self.drawborder.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) - self.drawborder.set_border_width(10) - self.drawborder.show() - self.drawframe = gtk.EventBox() - self.drawframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - self.drawframe.set_border_width(5) - self.drawframe.show() - self.drawborder.add(self.drawframe) - self.drawframe.add(self.mfdraw) - #self.drawhbox.pack_start(self.drawborder, False, False, 10) - - #self.vbox.pack_start(self.drawhbox,False,False,0) - self.vbox.pack_start(self.drawborder,True,False,0) - - self.keyframehbox = gtk.HBox() - self.keyframehbox.show() - - self.playback = gtk.Button() - self.playback.connect('clicked', self.playbackwards, None) - playbackla = gtk.Image() - playbackla.set_from_file(os.path.join(self.iconsdir,'big_left_arrow.png')) - playbackla.show() - self.playback.add(playbackla) - prepare_btn(self.playback) - self.playback.show() - self.playbackvbox = gtk.VBox() - self.playbackvbox.show() - self.playbackvbox.pack_start(self.playback,True,False,0) - self.keyframehbox.pack_start(self.playbackvbox,True,False,0) - # vvvvvvvvvvKEYFRAME DRAWING AREA HEREvvvvvvvvvvvv - self.kfdraw = gtk.DrawingArea() - self.kfdraw.set_size_request(KEYFRAMEWIDTH,KEYFRAMEHEIGHT) - self.kfdraw.show() - self.kfdraw.connect('expose_event', self.kf_expose_event) - self.kfdraw.connect('configure_event', self.kf_configure_event) - self.kfdraw.connect('motion_notify_event', self.kf_motion_notify_event) - self.kfdraw.connect('button_press_event', self.kf_button_press_event) - self.kfdraw.connect('button_release_event', self.kf_button_release_event) - self.kfdraw.set_events(gtk.gdk.EXPOSURE_MASK - | gtk.gdk.LEAVE_NOTIFY_MASK - | gtk.gdk.BUTTON_PRESS_MASK - | gtk.gdk.BUTTON_RELEASE_MASK - | gtk.gdk.POINTER_MOTION_MASK - | gtk.gdk.POINTER_MOTION_HINT_MASK) - #self.drawborder = gtk.EventBox() - #self.drawborder.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) - #self.drawborder.show() - #self.drawframe = gtk.EventBox() - #self.drawframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - #self.drawframe.set_border_width(5) - #self.drawframe.show() - #self.drawborder.add(self.drawframe) - #self.drawframe.add(self.mfdraw) - self.keyframehbox.pack_start(self.kfdraw,False,False,0) - - # ^^^^^^^^^^KEYFRAME DRAWING AREA HERE^^^^^^^^^^^^ - self.playforward = gtk.Button() - self.playforward.connect('clicked', self.playforwards, None) - playforwardla = gtk.Image() - playforwardla.set_from_file(os.path.join(self.iconsdir,'big_right_arrow.png')) - playforwardla.show() - self.playforward.add(playforwardla) - prepare_btn(self.playforward) - self.playforward.show() - self.playforwardvbox = gtk.VBox() - self.playforwardvbox.show() - self.playforwardvbox.pack_start(self.playforward,True,False,0) - self.keyframehbox.pack_start(self.playforwardvbox,True,False,0) - - self.vbox.pack_start(self.keyframehbox,False,False,10) - - self.bottomcontrolshbox = gtk.HBox() - self.bottomcontrolshbox.show() - self.resetbutton = gtk.Button() - resetimg = gtk.Image() - resetimg.set_from_file(os.path.join(self.iconsdir,'reset.png')) - resetimg.show() - self.resetbutton.add(resetimg) - self.resetbutton.connect('clicked', self.reset, None) - prepare_btn(self.resetbutton) - self.resetbutton.show() - self.bottomcontrolshbox.pack_start(self.resetbutton, True, False, 0) - - self.setframebutton = gtk.Button() - cameraimg = gtk.Image() - cameraimg.set_from_file(os.path.join(self.iconsdir,'camera.png')) - cameraimg.show() - self.setframebutton.add(cameraimg) - self.setframebutton.connect('clicked', self.setframe, None) - prepare_btn(self.setframebutton) - self.setframebutton.show() - self.bottomcontrolshbox.pack_start(self.setframebutton, True, False, 0) - - self.clearframebutton = gtk.Button() - clearimg = gtk.Image() - clearimg.set_from_file(os.path.join(self.iconsdir,'clear.png')) - clearimg.show() - self.clearframebutton.add(clearimg) - self.clearframebutton.connect('clicked', self.clearframe, None) - prepare_btn(self.clearframebutton) - self.clearframebutton.show() - self.bottomcontrolshbox.pack_start(self.clearframebutton, True, False, 0) - - adj = gtk.Adjustment(2.5,1,5,.5,1) - adj.connect('value_changed',self.setplayspeed) - self.playspeed = gtk.HScale(adj) - self.playspeed.set_draw_value(False) - for state, color in COLOR_BG_BUTTONS: - self.playspeed.modify_bg(state, gtk.gdk.color_parse(color)) - self.playspeed.show() - self.bottomcontrolshbox.pack_start(self.playspeed, True, True, 5) - self.vbox.pack_start(self.bottomcontrolshbox,False,False,10) - - # NOW THE RIGHT SIDE - self.rightvbox = gtk.VBox() - self.rightvbox.show() - # vvvvv FILE PICKER THING vvvvv - #self.filechooserhbox = gtk.HBox() - #self.filechooserhbox.show() - #self.lastfile = gtk.Button() - #lfla = gtk.Image() - #lfla.set_from_file(os.path.join(self.iconsdir,'left_arrow.png')) - #lfla.show() - #self.lastfile.add(lfla) - #prepare_btn(self.lastfile) - #self.lastfile.show() - #self.lfvbox = gtk.VBox() - #self.lfvbox.show() - #self.lfvbox.pack_start(self.lastfile,True,False,0) - #self.filechooserhbox.pack_start(self.lfvbox,True,False,5) - ## vvvvv FILE PICKER DRAWING AREA vvvvv - #self.fpdraw = gtk.DrawingArea() - #self.fpdraw.set_size_request(FPWIDTH,FPHEIGHT) - #self.fpdraw.show() - #self.fpdraw.connect('expose_event', self.fp_expose_event) - #self.fpdraw.connect('configure_event', self.fp_configure_event) - #self.fpdrawborder = gtk.EventBox() - #self.fpdrawborder.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) - #self.fpdrawborder.show() - #self.fpdrawframe = gtk.EventBox() - #self.fpdrawframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) - #self.fpdrawframe.set_border_width(5) - #self.fpdrawframe.show() - #self.fpdrawborder.add(self.fpdrawframe) - #self.fpdrawframe.add(self.fpdraw) - #self.filechooserhbox.pack_start(self.fpdrawborder,False,False,0) - ## ^^^^^ FILE PICKER DRAWING AREA ^^^^^ - #self.nextfile = gtk.Button() - #nfla = gtk.Image() - #nfla.set_from_file(os.path.join(self.iconsdir,'right_arrow.png')) - #nfla.show() - #self.nextfile.add(nfla) - #prepare_btn(self.nextfile) - #self.nextfile.show() - #self.nfvbox = gtk.VBox() - #self.nfvbox.show() - #self.nfvbox.pack_start(self.nextfile,True,False,0) - #self.filechooserhbox.pack_start(self.nfvbox,True,False,5) - #self.rightvbox.pack_start(self.filechooserhbox,False,False,5) - # ADD FILENAME LABEL HERE - # ^^^^^ FILE PICKER THING ^^^^^ - # START OF STICK CONTROLS - self.stickcontrols = gtk.VBox() - self.stickcontrols.show() - self.stickcontrolsborder = gtk.EventBox() - self.stickcontrolsborder.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) - self.stickcontrolsborder.set_border_width(5) - self.stickcontrolsborder.show() - self.stickcontrolsframe = gtk.EventBox() - self.stickcontrolsframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) - self.stickcontrolsframe.set_border_width(5) - self.stickcontrolsframe.show() - self.stickcontrolsborder.add(self.stickcontrolsframe) - self.stickcontrolsframe.add(self.stickcontrols) - self.anglesizehbox = gtk.HBox() - self.anglesizehbox.show() - #label = gtk.Label('Angle:') - self.anglelabel = gtk.Label(LANG[self.language]['angle']+':') - self.anglelabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) - self.anglelabel.show() - self.anglesizehbox.pack_start(self.anglelabel,False,False,5) - self.angleentry = gtk.Entry() - self.angleentry.set_max_length(3) - self.angleentry.set_width_chars(3) - self.angleentry.connect('activate', self.enterangle_callback, self.angleentry) - self.angleentry.show() - self.anglesizehbox.pack_start(self.angleentry,False,False,0) - #label = gtk.Label('Size:') - self.sizelabel = gtk.Label(LANG[self.language]['size']+':') - self.sizelabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) - self.sizelabel.show() - self.anglesizehbox.pack_start(self.sizelabel,False,False,5) - self.sizeentry = gtk.Entry() - self.sizeentry.set_max_length(3) - self.sizeentry.set_width_chars(3) - self.sizeentry.connect('activate', self.enterlen_callback, self.sizeentry) - self.sizeentry.show() - self.anglesizehbox.pack_start(self.sizeentry,False,False,0) - self.asbox = gtk.EventBox() - self.asbox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_FOREGROUND)) - self.asbox.show() - self.asbox.add(self.anglesizehbox) - self.stickcontrols.pack_start(self.asbox,False,False,0) - self.stickbuttons = {} - self.sticklabels = {} - for stickpartname in self.labellist: - #label = gtk.Label(capwords(stickpartname)) - label = gtk.Label(LANG[self.language][stickpartname]) - label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_FOREGROUND)) - label.show() - self.sticklabels[stickpartname] = label - ebox = gtk.EventBox() - ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) - ebox.show() - ebox.set_events(gtk.gdk.BUTTON_PRESS_MASK) - ebox.connect('button_press_event',self.selectstick,stickpartname) - ebox.add(label) - self.stickbuttons[stickpartname] = ebox - self.stickcontrols.pack_start(ebox,False,False,0) - # highlight the currently selected stick and update the entry boxes - self.selectstickebox() - # END OF STICK CONTROLS - stickalign = gtk.Alignment(1.0,1.0,1.0,1.0) - stickalign.add(self.stickcontrolsborder) - stickalign.set_padding(80,5,5,5) # top,bottom,left,right - stickalign.show() - self.rightvbox.pack_start(stickalign,False,False, 0) - #self.rightvbox.pack_start(self.stickcontrolsborder,False,False, 0) - #self.filesave = gtk.Button() - #self.filesave.set_label('SAVE') - #prepare_btn(self.filesave) - #self.filesave.connect('clicked',self.savefile,None) - #self.filesave.show() - #self.fshbox = gtk.HBox() - #self.fshbox.show() - #self.fshbox.pack_start(self.filesave,True,False,0) - #self.rightvbox.pack_start(self.fshbox,True,False,0) - self.export = gtk.Button() - #self.export.set_label('EXPORT') - self.export.set_label(LANG[self.language]['export']) - prepare_btn(self.export) - self.export.connect('clicked',self.exportanim,None) - self.export.show() - self.exporthbox = gtk.HBox() - self.exporthbox.show() - self.exporthbox.pack_start(self.export,True,False,0) - self.rightvbox.pack_start(self.exporthbox,True,False,0) - - self.hbox.pack_start(self.rightvbox,False,False,0) - -try: - from sugar.activity import activity - from sugar.presence import presenceservice - from sugar.presence.tubeconn import TubeConnection - import telepathy - import telepathy.client - from dbus import Interface - from dbus.service import method, signal - from dbus.gobject_service import ExportedGObject - - class flipsticksActivity(activity.Activity): - def __init__(self, handle): - activity.Activity.__init__(self,handle) - self.connect("destroy",self.destroy_cb) - bundle_path = activity.get_bundle_path() - os.chdir(bundle_path) - self.set_title('FlipSticks') - toolbox = activity.ActivityToolbox(self) - self.set_toolbox(toolbox) - toolbox.show() - if hasattr(self, '_jobject'): - self._jobject.metadata['title'] = 'FlipSticks' - title_widget = toolbox._activity_toolbar.title - title_widget.set_size_request(title_widget.get_layout().get_pixel_size()[0] + 20, -1) - self.app = flipsticks(self, bundle_path) - self.app.insugar = True - outerframe = gtk.EventBox() - outerframe.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) - outerframe.show() - innerframe = gtk.EventBox() - innerframe.show() - ifalign = gtk.Alignment(1.0,1.0,1.0,1.0) - ifalign.add(innerframe) - ifalign.set_padding(20,20,50,50) # top,bottom,left,right - ifalign.show() - #innerframe.set_border_width(150) - outerframe.add(ifalign) - innerframe.add(self.app.main) - self.set_canvas(outerframe) - - - # mesh stuff - self.pservice = presenceservice.get_instance() - owner = self.pservice.get_owner() - self.owner = owner - try: - name, path = self.pservice.get_preferred_connection() - self.tp_conn_name = name - self.tp_conn_path = path - self.conn = telepathy.client.Connection(name, path) - except TypeError: - pass - self.initiating = None - - #sharing stuff - self.game = None - self.connect('shared', self._shared_cb) - if self._shared_activity: - # we are joining the activity - self.connect('joined', self._joined_cb) - if self.get_shared(): - # oh, OK, we've already joined - self._joined_cb() - else: - # we are creating the activity - pass - - def destroy_cb(self, data=None): - return True - - def read_file(self, filepath): - f = file(filepath) - sdata = f.read() - f.close() - self.app.restore(sdata) - - def write_file(self, filepath): - sdata = self.app.getsdata() - f = open(filepath,'w') - f.write(sdata) - f.close() - - def _shared_cb(self,activity): - self.initiating = True - self._setup() - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( - SERVICE, {}) - #self.app.export.set_label('Shared Me') - - def _joined_cb(self,activity): - if self.game is not None: - return - - if not self._shared_activity: - return - - #for buddy in self._shared_activity.get_joined_buddies(): - # self.buddies_panel.add_watcher(buddy) - - #logger.debug('Joined an existing Connect game') - #self.app.export.set_label('Joined You') - self.initiating = False - self._setup() - - #logger.debug('This is not my activity: waiting for a tube...') - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( - reply_handler=self._list_tubes_reply_cb, - error_handler=self._list_tubes_error_cb) - - def _setup(self): - if self._shared_activity is None: - return - - bus_name, conn_path, channel_paths = self._shared_activity.get_channels() - - # Work out what our room is called and whether we have Tubes already - room = None - tubes_chan = None - text_chan = None - for channel_path in channel_paths: - channel = telepathy.client.Channel(bus_name, channel_path) - htype, handle = channel.GetHandle() - if htype == telepathy.HANDLE_TYPE_ROOM: - #logger.debug('Found our room: it has handle#%d "%s"', - # handle, self.conn.InspectHandles(htype, [handle])[0]) - room = handle - ctype = channel.GetChannelType() - if ctype == telepathy.CHANNEL_TYPE_TUBES: - #logger.debug('Found our Tubes channel at %s', channel_path) - tubes_chan = channel - elif ctype == telepathy.CHANNEL_TYPE_TEXT: - #logger.debug('Found our Text channel at %s', channel_path) - text_chan = channel - - if room is None: - #logger.error("Presence service didn't create a room") - return - if text_chan is None: - #logger.error("Presence service didn't create a text channel") - return - - # Make sure we have a Tubes channel - PS doesn't yet provide one - if tubes_chan is None: - #logger.debug("Didn't find our Tubes channel, requesting one...") - tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, - telepathy.HANDLE_TYPE_ROOM, room, True) - - self.tubes_chan = tubes_chan - self.text_chan = text_chan - - tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', - self._new_tube_cb) - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._new_tube_cb(*tube_info) - - def _list_tubes_error_cb(self, e): - #logger.error('ListTubes() failed: %s', e) - pass - - def _new_tube_cb(self, id, initiator, type, service, params, state): - #logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' - # 'params=%r state=%d', id, initiator, type, service, - # params, state) - - if (self.game is None and type == telepathy.TUBE_TYPE_DBUS and - service == SERVICE): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) - - tube_conn = TubeConnection(self.conn, - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], - id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - self.game = ConnectGame(tube_conn, self.initiating, self) - - class ConnectGame(ExportedGObject): - def __init__(self,tube, is_initiator, activity): - super(ConnectGame,self).__init__(tube,PATH) - self.tube = tube - self.is_initiator = is_initiator - self.entered = False - self.activity = activity - - self.ordered_bus_names=[] - self.tube.watch_participants(self.participant_change_cb) - - def participant_change_cb(self, added, removed): - if not self.entered: - if self.is_initiator: - self.add_hello_handler() - else: - self.Hello() - self.entered = True - - @signal(dbus_interface=IFACE,signature='') - def Hello(self): - """Request that this player's Welcome method is called to bring it - up to date with the game state. - """ - - @method(dbus_interface=IFACE, in_signature='s', out_signature='') - def Welcome(self, sdata): - self.activity.app.restore(str(sdata)) - - def add_hello_handler(self): - self.tube.add_signal_receiver(self.hello_cb, 'Hello', IFACE, - path=PATH, sender_keyword='sender') - - def hello_cb(self, sender=None): - self.tube.get_object(sender, PATH).Welcome(self.activity.app.getsdata(),dbus_interface=IFACE) - -except ImportError: - pass - -if __name__ == '__main__': - toplevel_window = gtk.Window(gtk.WINDOW_TOPLEVEL) - mdirpath = os.path.abspath(os.curdir) - app = flipsticks(toplevel_window,mdirpath) - toplevel_window.add(app.main) - toplevel_window.set_title('FlipSticks') - toplevel_window.connect('delete_event', app.delete_event) - toplevel_window.connect('destroy', app.destroy) - toplevel_window.show() - gtk.main() diff --git a/flipsticks01.jpg b/flipsticks01.jpg deleted file mode 100644 index f453ec6..0000000 --- a/flipsticks01.jpg +++ /dev/null Binary files differ diff --git a/flipsticks04.jpg b/flipsticks04.jpg deleted file mode 100644 index 1092b3b..0000000 --- a/flipsticks04.jpg +++ /dev/null Binary files differ diff --git a/flipsticksIcon.eps b/flipsticksIcon.eps deleted file mode 100644 index 70d6ce3..0000000 --- a/flipsticksIcon.eps +++ /dev/null @@ -1,59 +0,0 @@ -%!PS-Adobe-3.1 EPSF-3.0 %ADO_DSC_Encoding: MacOS Roman %%Title: flipsticksIcon.eps %%Creator: Adobe Illustrator(R) 12 %%AI8_CreatorVersion: 12.0.0 %AI9_PrintingDataBegin %%For: Richard Goehl %%CreationDate: 10/8/07 %%BoundingBox: 0 0 61 61 %%HiResBoundingBox: 0 0 60.7500 60.5293 %%CropBox: 0 0 60.7500 60.5293 %%LanguageLevel: 2 %%DocumentData: Clean7Bit %%Pages: 1 %%DocumentNeededResources: %%DocumentSuppliedResources: procset Adobe_AGM_Image 1.0 0 %%+ procset Adobe_CoolType_Utility_T42 1.0 0 %%+ procset Adobe_CoolType_Utility_MAKEOCF 1.19 0 %%+ procset Adobe_CoolType_Core 2.23 0 %%+ procset Adobe_AGM_Core 2.0 0 %%+ procset Adobe_AGM_Utils 1.0 0 %%DocumentFonts: %%DocumentNeededFonts: %%DocumentNeededFeatures: %%DocumentSuppliedFeatures: %%DocumentProcessColors: Black %%DocumentCustomColors: %%CMYKCustomColor: %%RGBCustomColor: %ADO_BuildNumber: Adobe Illustrator(R) 12.0.0 x5199 R agm 4.3861 ct 5.530 %ADO_ContainsXMP: MainFirst %AI7_Thumbnail: 128 128 8 %%BeginData: 15708 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD8027F8272027F8272027F8272027F8272027F8272027F8272027 %F8272027F8272027F8272027F8272027F8272027F8272027F8272027F827 %2027F8272027F8272027F8272027F8272027F8272027F8272027F8272027 %F8272027F8272027F8272027F8272027F8272027F8272027F8272027F827 %2027F8272027F8272027F827202727202727272027272720272727202727 %272027272720272727202727272027272720272727202727272027272720 %272727202727272027272720272727202727272027272720272727202727 %272027272720272727202727272027272720272727202727272027272720 %27272720272727202727272027272720272727202727F827F827F827F827 %F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8FD %81272027F8272027F8272027F8272027F8272027F8272027F8272027F827 %2027F8272027F8272027F8272027F8272027F8272027F8272027F8272027 %F8272027F8272027F8272027F8272027F8272027F8272027F8272027F827 %2027F8272027F8272027F8272027F8272027F8272027F8272027F8272027 %F8272027F8272027F8FD0427202727272027272720272727202727272027 %272720272727202727272027272720272727202727272027272720272727 %202727272027272720272727202727272027272720272727202727272027 %272720272727202727272027272720272727202727272027272720272727 %2027272720272727202727272027272720F827F827F827F8537D7E7D7D7D %7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D %7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D %7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7D %7D7D7E7D7D7D7E7D7D7D7E7D7D7D7E7DA85227F827F827F8FD08277DFD70 %FFA8FD0727F8272027F827207DFD70FF7D272027F8272027272027272720 %277DFD70FFA820272727202727F827F827F827F87EFD70FF7D27F827F827 %F8FD08277DFD70FFA8FD07272027F8272027F8A8FD2CFFA8A8A8FD41FF7D %27F8272027F8FD0427202727277DFD28FFA85252FD0427527DA8FD3EFFA8 %27272027272720F827F827F827F87DFD26FF7D27F827F827F827F827F827 %52A8FD3CFF7D27F827F827F8FD08277DFD25FF52FD0E27A8FD3BFFA8FD07 %27F8272027F827207DFD24FF27272027F8272027F8272027F8272027F87D %FD3AFF7D272027F8272027272027272720277DFD23FF522721FD04277DFD %04A852272027272720A8FD39FFA820272727202727F827F827F827F87EFD %22FF5227F827F8277DFD07FFA852F827F82720FD39FF7D27F827F827F8FD %08277DFD21FFA8FD04274BA8FD0AFF7DFD042752FD38FFA8FD07272027F8 %272027F8A8FD21FF59F82720277DFD0CFF522027F827A8FD37FF7D27F827 %2027F8FD0427202727277DFD21FF2727202752FD0DFFA8272727207EFD37 %FFA827272027272720F827F827F827F87DFD20FF7D27F827F8A8FD0EFF52 %27F82752FD37FF7D27F827F827F8FD08277DFD20FF8427272752FD0FFFA8 %27272752FD37FFA8FD0727F8272027F827207DFD20FF5327202752FD0FFF %7D27202727FD37FF7D272027F8272027272027272720277DFD20FF7D2027 %2752FD0FFFA820272727A8FD36FFA820272727202727F827F827F827F87E %FD20FF7D27F82727FD0FFF7D27F82727FD37FF7D27F827F827F8FD08277D %FD20FFA8FD0427A8FD0EFF7D27272752FD37FFA8FD07272027F8272027F8 %A8FD20FFA827F827207DFD0EFF2727F82752FD37FF7D27F8272027F8FD04 %27202727277DFD21FF5227212727FD0DFF5327272720A8FD37FFA8272720 %27272720F827F827F827F87DFD21FFA8F827F82728FD0BFF7D27F827F852 %FD38FF7D27F827F827F8FD08277DFD22FF52FD042752FD09FF7DFD0527A8 %FD38FFA8FD0727F8272027F827207DFD22FFA827F8272027277DA8FFFFFF %A8A852272027F82752FD39FF7D272027F8272027272027272720277DFD23 %FF7E272027272720272752FD04272027272752FD3AFFA820272727202727 %F827F827F827F87EFD24FF7D27F827F827F827F827F827F827F82752FD09 %FFA8FD31FF7D27F827F827F8FD08277DFD25FFA852FD0C277DFD08FFA852 %52527DFD2FFFA8FD07272027F8272027F8A8FD27FFA85227F8272027F827 %202727FD08FF7D2027F8272052FD2EFF7D27F8272027F8FD042720272727 %7DFD2AFFA87DA87D7DFD0427A8FFFFFFA8A87D5220277DA87D2727A8FD2D %FFA827272027272720F827F827F827F87DFD2FFF27F827F85252522727F8 %27F82752FFFFFF522752FD2DFF7D27F827F827F8FD08277DFD23FF7DA8A8 %FD05FFA8A87D7DFD0D277DFFFFFFA82753FD2DFFA8FD0727F8272027F827 %207DFD21FF7D2027F8277D7D52522727F8272027F8272027F8272027F827 %202727A8FFA827277DFD2DFF7D272027F8272027272027272720277DFD20 %FF7D202752522027272720272727202727272027272720272727527D2727 %215227272052A8FD1BFFA8FD10FFA820272727202727F827F827F827F87E %FD20FF272753FFFF7DF827F827F827F827F827F827F827527D7DA8A8FFFF %FF2727F827F827F827A8FD18FF7D2727F852A8FD0DFF7D27F827F827F8FD %08277DFD1FFFA82727FFFFFFA8FD0727287D7DFD0427FD09FFA8A852FD05 %27A8FD16FF7DFD0627A8FD0CFFA8FD07272027F8272027F8A8FD1FFFAF20 %27A8FFFFA8F827527D7DA8A8FFFFFF2727F8277DFD0BFF2727F8272027A8 %FD13FFA87D202752FFA852F852FD0CFF7D27F8272027F8FD042720272727 %7DFD20FF522752A8A8272752FD08FF522727207DFD0CFF272721272727A8 %FD10FFA85227202727FFFFFFA82727FD0CFFA827272027272720F827F827 %F827F87DFD1FFF7DF827F827F82727A8FD08FF5227F82752FD0DFF2727F8 %27F827A8FD0CFFA85227F827F827F827FFFFFFA8F827A8FD0BFF7D27F827 %F827F8FD08277DFD1EFFA8FD05275252FD0AFFA827272752FD0EFFFD0527 %52A8FD09FF7E7DFD09277DFFFF522752FD0CFFA8FD0727F8272027F82720 %7DFD1EFF5227F82720A8FD0CFF7D27202720FD0FFF27272027F827A8FD05 %FFA87D2727F8272027F827202752272027F82720A8FD0CFF7D272027F827 %2027272027272720277DFD1DFF7D272727207DFD0EFFFD0427A8FD0FFFFD %0427202727527DA852272027272720FD0427A8FFFF7D52272727A8FD0DFF %A820272727202727F827F827F827F87EFD1DFF52F827F827A8FD0EFF27F8 %27F87DFD10FF2727F827F827F827F827F827F827F827F8527DFD05FFA8A8 %A8FD0FFF7D27F827F827F8FD08277DFD1CFF7DFD04277DFD0FFF52272727 %53FD11FF5227277DA8A8FD0727527DFD1AFFA8FD07272027F8272027F8A8 %FD1CFF2727F82727FD10FF7DF8272052FD11FF7D2052FFFFFF7DF8272027 %52A8FD1CFF7D27F8272027F8FD0427202727277DFD1BFF5227272720A8FD %10FF7D27202727FD11FF7D2752FFFFFF7D27207DA8FD1EFFA82727202727 %2720F827F827F827F87DFD1AFFA827F827F852FD11FFA8F827F827A8FD10 %FFA8F8277DFFA852F87DFD20FF7D27F827F827F8FD08277DFD19FF7D52FD %0427A8FD11FFA8FD0427A8FD11FF52FD0627A8FD20FFA8FD0727F8272027 %F827207DFD17FF7DF8272027F82752FD13FF2727F8277DFD11FFA8522027 %F852A8FD21FF7D272027F8272027272027272720277DFD16FFA827272852 %272727FD14FF7D2727207DFD13FFAFA8A8FD23FFA820272727202727F827 %F827F827F87EFD16FF272752FFFF7DF827A8FD13FF5227F82728FD39FF7D %27F827F827F8FD08277DFD15FF7D2727FD04FF5227A8FD13FFA827272752 %FD39FFA8FD07272027F8272027F8A8FD13FFA82727F8277DFFFFFF2027A8 %FD13FF84272027F8AFFD04FF7D5352A8FD30FF7D27F8272027F8FD042720 %2727277DFD11FFA8522027272720277DA8272752FD15FFFD04277DFFFFA8 %FD0427207DFD2FFFA827272027272720F827F827F827F87DFD10FF5227F8 %27F827F827F827F827F8A8FD10FFA8FD04FF52F827F87DFFFF2727277D52 %27F87EFD2EFF7D27F827F827F8FD08277DFD0EFF7DFD0727527D7D525252 %FD0FFFA87D272752A8FFFF52FD04277D272727FFFFFF7D2752FD2EFFA8FD %0727F8272027F827207DFD0CFF7D522027F8272027277DFD14FFA827F827 %2027F87D5227F8272027F8272052FFFFFFA8F827A8FD2DFF7D272027F827 %2027272027272720277DFD07FF7D7D7DA8522720272727202752FD16FF7D %275284FF52272027272720272727202727FFFFFF532720277DFD2CFFA820 %272727202727F827F827F827F87EFD05FF7DF827F827F827F827F827F852 %A8FD17FF27277DFFFFFF2727F827F827F827F827F827277D5227F827F827 %52FD2BFF7D27F827F827F8FD08277DFD04FFA82727527DFD0727A8FD19FF %5227A8FFFFFF52FD052752527D7DA8FD0B27A8FD29FFA8FD07272027F827 %2027F8A8FD04FF522752FFFFFF272720277DFD1BFF272752FFFF7D202752 %7D7DA8A8FD05FF7D5952A85227F8272027F87DFD28FF7D27F8272027F8FD %0427202727277DFD04FF5227A8FFFFFF7D2052FD1CFF5227212727522027 %7DFD0FFF7D27202727272052A8FD26FFA827272027272720F827F827F827 %F87DFD04FF522752FFFFFF27277DFD1BFF7D27F827F827F82752FD11FFA8 %52F827F827F82752FD25FF7D27F827F827F8FD08277DFD04FFA8272752A8 %522727FD1BFFAF52FD04277EA8A8FD14FF7DFD0727AFFD23FFA8FD0727F8 %272027F827207DFD05FF7D2027F82720A8FD1BFF59F82720277DFD18FFA8 %2727F8272027F87DFD22FF7D272027F8272027272027272720277DFD06FF %A8525252A9FD1BFFA82727212752FD1BFF5927202727272052A8FD20FFA8 %20272727202727F827F827F827F87EFD26FF2727F827F8A8FD1CFF7D27F8 %27F827F8277DFD1FFF7D27F827F827F8FD08277DFD25FF7DFD04277DFD1F %FF7DFD062752FD1EFFA8FD07272027F8272027F8A8FD24FFA827F8272027 %A8FD20FFA82727F8272027277E7D84A8FD19FF7D27F8272027F8FD042720 %2727277DFD24FF522727202784FD23FF522720272727202727277DFD18FF %A827272027272720F827F827F827F87DFD23FF7DF827F82752FD25FF7D27 %F827F8272752F8277DFD17FF7D27F827F827F8FD08277DFD23FFFD0527FD %27FFA852272752FFFF7D2752FD17FFA8FD0727F8272027F827207DFD22FF %5227F827207DFD28FFA82720FFFFFFA82727FD17FF7D272027F827202727 %2027272720277DFD21FF7D2727272052FD12FFA87D28527DFD13FF27277D %FFFF7D2752A8FD16FFA820272727202727F827F827F827F87EFD21FF27F8 %27F827A8FD11FFA827F827F82727FD12FF7DF8275252F827F87DFD16FF7D %27F827F827F8FD08277DFD20FF7DFD04277DFD12FF522752A8A827277DFD %12FF7DFD0727FD16FFA8FD07272027F8272027F8A8FD1FFFA82027F82727 %FD11FFA87D2027A8FFFF7DF852FD13FFA85353F82720277DFD15FF7D27F8 %272027F8FD0427202727277DFD1FFF5227272720A8FD0AFFA8A87D7D5252 %2727202727FFFFFFA82727FD16FF5220272752FD15FFA827272027272720 %F827F827F827F87DFD1EFF5227F827F87DFD04FFA8FF847D52522727F827 %F827F827F827F82752FFA852F852FD16FF7D27F827F884FD14FF7D27F827 %F827F8FD08277DFD1DFFFD0627A8A87D7D5252FD1327522727A8FD17FFFD %042752FD14FFA8FD0727F8272027F827207DFD1CFF2727277D52272027F8 %272027F8272027F8272027F8272027F827202727522727F8277DFD18FF7D %F82720277DFD13FF7D272027F8272027272027272720277DFD1BFFA82720 %A8FFFF7D272727202727272027272720FD042752527D7DA9FFFFA8272727 %20A8FD19FF2720272753FD13FFA820272727202727F827F827F827F87EFD %1BFFA8F852FFFFFFA8F827F827F827F827F827527D7DA8A8FD08FFA8F827 %F827A8FD19FF5227F827F8A8FD0BFFA8FD06FF7D27F827F827F8FD08277D %FD1BFFA82727FFFFFF7DFD04277D7D84A8FD0FFF7DFD0427FD1BFFFD0427 %52FD09FFA85227FD06FFA8FD07272027F8272027F8A8FD1CFF2827277D52 %27F87DA8FD14FF7DF8272052FD1BFF52F8272027A8FD07FF7D27202752FD %05FF7D27F8272027F8FD0427202727277DFD1DFF27272027277DFD16FF52 %27202752FD1BFFA8272027277DFD06FF5227202727277DFD04FFA8272720 %27272720F827F827F827F87DFD1EFF7D5252A8FD16FFA852F827F87DFD1C %FF5227F82727FD04FFA82727F827F827F87DFD04FF7D27F827F827F8FD08 %277DFD39FFFD0427A8FD1CFFA8FD042752A8FF7DFD062752FD06FFA8FD07 %27F8272027F827207DFD38FF84272027F8AFFD1DFF52F8272027F8272027 %F82720277DFD07FF7D272027F8272027272027272720277DFD38FFA82027 %2727FD1EFF272727A85327202727272052A8FD08FFA820272727202727F8 %27F827F827F87EFD38FFA8A87D7D52FD1DFF7D2727FFFFFF5227F827F87D %FD0AFF7D27F827F827F8FD08277DFD5AFFA82753FFFFFFA8272727A8FD0B %FFA8FD07272027F8272027F8A8FD5AFF7D2727A8FFFF522727FD0DFF7D27 %F8272027F8FD0427202727277DFD5BFF2727277D522727A8FD0DFFA82727 %2027272720F827F827F827F87DFD5BFFA82727F827F87DFD0EFF7D27F827 %F827F8FD08277DFD07FFA8A8A8FD1BFFFD05A8FD1BFFA8A884A9FD14FF7D %7D7DFD05FFA8A8A8FD08FFA8FD0727F8272027F827207DFD04FF59522727 %F827275252A8FD14FFA87D27272027F827277DA8FD15FFA87D27272027F8 %27527DA8FD14FFA85227F827202727527DFD05FF7D272027F82720272720 %27272720277DFFFF7D27272027272720272727207DFD12FF7D2727272027 %27272027272752FD13FF5227272720272727202727277DFD11FFA8522027 %272720272727202752A8FFFFFFA820272727202727F827F827F827F8A8FF %52F827F827F827F827F827F827F852A8FD0FFF5227F827F827F827F827F8 %27F82727A9FD0FFFA82727F827F827F827F827F827F82752FD0FFF7D27F8 %27F827F827F827F827F827F87DFFFF7D27F827F827F8FD08277D52FD1027 %A8FD0DFF52FD1027FD0FFFFD102752FD0DFF7DFD1027A8FFA8FD07272027 %F8272027F8272027F8272027F8272027F8272027F8272052FD0CFF522720 %27F8272027F8272027F8272027F82727FD0DFF27272027F8272027F82720 %27F8272027F82752FD0BFF7D27F8272027F8272027F8272027F8272027F8 %A87D27F8272027F8FD042720272727202727272027272720272727202727 %272027277DFD0AFFA8272027272720272727202727272027272720277DFD %0BFF7D27202727272027272720272727202727272027A8FD0AFF52272720 %27272720272727202727272027272752A827272027272720F827F827F827 %F827F827F827F827F827F827F827F827F827F827277D7D7E7D7D7D7E7D7D %7D52F827F827F827F827F827F827F827F827F827F8277D7D7D7E7D7D7D7E %7D7D7D27F827F827F827F827F827F827F827F827F827F8527D7D7D7E7D7D %7D7E7D53F827F827F827F827F827F827F827F827F827F8272727F827F827 %F8FD0F27527DFFA87DFD1A2752A8A8A852FD1B2752A8A8A852FD1A27A8A8 %AF7DFD1027F8272027F8272027F8272027F87DFD05FF7DF8272027F82720 %27F8272027F8272027F8272027F827202752FD05FF7D27F8272027F82720 %27F8272027F8272027F8272027F82720277DFD05FF5227F8272027F82720 %27F8272027F8272027F8272027F82727A8FD04FFA8272027F8272027F827 %2027F827202727202727272027272720272752FD07FF5220272727202727 %27202727272027272720272727202727FD07FF5227202727272027272720 %2727272027272720272727202752FD07FF27272027272720272727202727 %27202727272027272720A8FD06FF7D2727272027272720272727202727F8 %27F827F827F827F827F82752FD07FF7D27F827F827F827F827F827F827F8 %27F827F827F827F87DFD07FF7DF827F827F827F827F827F827F827F827F8 %27F827F827F87DFD07FF53F827F827F827F827F827F827F827F827F827F8 %27F827A8FD07FFF827F827F827F827F827F827F8FD0D277DFD07FFA8FD16 %2759FD07FF7DFD17277DFD07FF53FD1627FD08FFFD0E272027F8272027F8 %272027F82727FD07FF5227F8272027F8272027F8272027F8272027F82720 %27F852FD07FF7D2027F8272027F8272027F8272027F8272027F8272027F8 %7DFD07FF522027F8272027F8272027F8272027F8272027F8272027A8FD06 %FFA8F8272027F8272027F8272027F8FD08272027272720277DFD05FFA827 %272720FD0F272027272720A8FD05FFA82027272720FD0F2720FD0427A8FD %05FF7D2027272720FD0B272027272720272752FD06FF52272027272720FD %0827A8FFA8FFFFA8F827F827F827F8277DFFFFFF7D27F827F827F827F87D %FFFFA8FFA8FFA8FF7D27F827F827F827F87DA8FFA87E2127F827F827F827 %53FFA8FFA8FFA8FFA8FF5227F827F827F82727A8FFFFA87DF827F827F827 %F8277DFFA8FFA8FFA8FFA827F827F827F827F852A8FFFFA82727F827F827 %F82727FFA8FFA8FFA8FD07FF52FD092752FD0A27A8FD09FFFD0A272852FD %0927FD0BFFFD09275228FD0A27FD09FF59FD09272852FD09277DFD0DFF7D %27F8272027F8272027F8272027F8272027F82752FD0AFF7D2027F8272027 %F8272027F8272027F8272027F853FD0BFF522027F8272027F8272027F827 %2027F8272027F87DFD09FFA8F8272027F8272027F8272027F8272027F827 %2027A8FD0EFF282721272727202727272027272720FD0427FD0CFF522727 %20272727202727272027272720272727A8FD0BFFA8272727202727272027 %27272027272720272752FD0BFF7D20272727202727272027272720272727 %20277DFD0FFFA8F827F827F827F827F827F827F827F827F8A8FD0CFFA827 %F827F827F827F827F827F827F827F8277DFD0DFF7D27F827F827F827F827 %F827F827F827F827A8FD0CFF52F827F827F827F827F827F827F827F82752 %FD11FFA8FD0F27A8FD0EFFA8FD0F277DFD0FFF7DFD0E2752A8FD0EFF53FD %0E2752FD13FFA828272027F8272027F827202727A8FD10FFA852F8272027 %F8272027F8272052A8FD11FF7E27F8272027F8272027F8272052A8FD10FF %7D2027F8272027F8272027F8277DFD16FF7D5227272027272720527DFD14 %FFA85227272720FD04277EFD15FF7DFD04272027272752A8FD14FF522720 %2727272027277DA8FD19FFA87D7D527D7DA8FD18FFA8847D7D527DA8FD19 %FFA87D597D53A8A8FD17FFA8A87D7D537D7DFD0EFFFF %%EndData %%EndComments %%BeginDefaults %%ViewingOrientation: 1 0 0 1 %%EndDefaults %%BeginProlog %%BeginResource: procset Adobe_AGM_Utils 1.0 0 %%Version: 1.0 0 %%Copyright: Copyright (C) 2000-2003 Adobe Systems, Inc. All Rights Reserved. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Utils 70 dict dup begin put /bdf { bind def } bind def /nd{ null def }bdf /xdf { exch def }bdf /ldf { load def }bdf /ddf { put }bdf /xddf { 3 -1 roll put }bdf /xpt { exch put }bdf /ndf { exch dup where{ pop pop pop }{ xdf }ifelse }def /cdndf { exch dup currentdict exch known{ pop pop }{ exch def }ifelse }def /ps_level /languagelevel where{ pop systemdict /languagelevel get exec }{ 1 }ifelse def /level2 ps_level 2 ge def /level3 ps_level 3 ge def /ps_version {version cvr} stopped { -1 }if def /set_gvm { currentglobal exch setglobal }bdf /reset_gvm { setglobal }bdf /makereadonlyarray { /packedarray where{ pop packedarray }{ array astore readonly }ifelse }bdf /map_reserved_ink_name { dup type /stringtype eq{ dup /Red eq{ pop (_Red_) }{ dup /Green eq{ pop (_Green_) }{ dup /Blue eq{ pop (_Blue_) }{ dup () cvn eq{ pop (Process) }if }ifelse }ifelse }ifelse }if }bdf /AGMUTIL_GSTATE 22 dict def /get_gstate { AGMUTIL_GSTATE begin /AGMUTIL_GSTATE_clr_spc currentcolorspace def /AGMUTIL_GSTATE_clr_indx 0 def /AGMUTIL_GSTATE_clr_comps 12 array def mark currentcolor counttomark {AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 3 -1 roll put /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 add def} repeat pop /AGMUTIL_GSTATE_fnt rootfont def /AGMUTIL_GSTATE_lw currentlinewidth def /AGMUTIL_GSTATE_lc currentlinecap def /AGMUTIL_GSTATE_lj currentlinejoin def /AGMUTIL_GSTATE_ml currentmiterlimit def currentdash /AGMUTIL_GSTATE_do xdf /AGMUTIL_GSTATE_da xdf /AGMUTIL_GSTATE_sa currentstrokeadjust def /AGMUTIL_GSTATE_clr_rnd currentcolorrendering def /AGMUTIL_GSTATE_op currentoverprint def /AGMUTIL_GSTATE_bg currentblackgeneration cvlit def /AGMUTIL_GSTATE_ucr currentundercolorremoval cvlit def currentcolortransfer cvlit /AGMUTIL_GSTATE_gy_xfer xdf cvlit /AGMUTIL_GSTATE_b_xfer xdf cvlit /AGMUTIL_GSTATE_g_xfer xdf cvlit /AGMUTIL_GSTATE_r_xfer xdf /AGMUTIL_GSTATE_ht currenthalftone def /AGMUTIL_GSTATE_flt currentflat def end }def /set_gstate { AGMUTIL_GSTATE begin AGMUTIL_GSTATE_clr_spc setcolorspace AGMUTIL_GSTATE_clr_indx {AGMUTIL_GSTATE_clr_comps AGMUTIL_GSTATE_clr_indx 1 sub get /AGMUTIL_GSTATE_clr_indx AGMUTIL_GSTATE_clr_indx 1 sub def} repeat setcolor AGMUTIL_GSTATE_fnt setfont AGMUTIL_GSTATE_lw setlinewidth AGMUTIL_GSTATE_lc setlinecap AGMUTIL_GSTATE_lj setlinejoin AGMUTIL_GSTATE_ml setmiterlimit AGMUTIL_GSTATE_da AGMUTIL_GSTATE_do setdash AGMUTIL_GSTATE_sa setstrokeadjust AGMUTIL_GSTATE_clr_rnd setcolorrendering AGMUTIL_GSTATE_op setoverprint AGMUTIL_GSTATE_bg cvx setblackgeneration AGMUTIL_GSTATE_ucr cvx setundercolorremoval AGMUTIL_GSTATE_r_xfer cvx AGMUTIL_GSTATE_g_xfer cvx AGMUTIL_GSTATE_b_xfer cvx AGMUTIL_GSTATE_gy_xfer cvx setcolortransfer AGMUTIL_GSTATE_ht /HalftoneType get dup 9 eq exch 100 eq or { currenthalftone /HalftoneType get AGMUTIL_GSTATE_ht /HalftoneType get ne { mark AGMUTIL_GSTATE_ht {sethalftone} stopped cleartomark } if }{ AGMUTIL_GSTATE_ht sethalftone } ifelse AGMUTIL_GSTATE_flt setflat end }def /get_gstate_and_matrix { AGMUTIL_GSTATE begin /AGMUTIL_GSTATE_ctm matrix currentmatrix def end get_gstate }def /set_gstate_and_matrix { set_gstate AGMUTIL_GSTATE begin AGMUTIL_GSTATE_ctm setmatrix end }def /AGMUTIL_str256 256 string def /AGMUTIL_src256 256 string def /AGMUTIL_dst64 64 string def /AGMUTIL_srcLen nd /AGMUTIL_ndx nd /thold_halftone { level3 {sethalftone currenthalftone} { dup /HalftoneType get 3 eq { sethalftone currenthalftone } { begin Width Height mul { Thresholds read {pop} if } repeat end currenthalftone } ifelse }ifelse } def /rdcmntline { currentfile AGMUTIL_str256 readline pop (%) anchorsearch {pop} if } bdf /filter_cmyk { dup type /filetype ne{ exch () /SubFileDecode filter } { exch pop } ifelse [ exch { AGMUTIL_src256 readstring pop dup length /AGMUTIL_srcLen exch def /AGMUTIL_ndx 0 def AGMCORE_plate_ndx 4 AGMUTIL_srcLen 1 sub{ 1 index exch get AGMUTIL_dst64 AGMUTIL_ndx 3 -1 roll put /AGMUTIL_ndx AGMUTIL_ndx 1 add def }for pop AGMUTIL_dst64 0 AGMUTIL_ndx getinterval } bind /exec cvx ] cvx } bdf /filter_indexed_devn { cvi Names length mul names_index add Lookup exch get } bdf /filter_devn { 4 dict begin /srcStr xdf /dstStr xdf dup type /filetype ne{ 0 () /SubFileDecode filter }if [ exch [ /devicen_colorspace_dict /AGMCORE_gget cvx /begin cvx currentdict /srcStr get /readstring cvx /pop cvx /dup cvx /length cvx 0 /gt cvx [ Adobe_AGM_Utils /AGMUTIL_ndx 0 /ddf cvx names_index Names length currentdict /srcStr get length 1 sub { 1 /index cvx /exch cvx /get cvx currentdict /dstStr get /AGMUTIL_ndx /load cvx 3 -1 /roll cvx /put cvx Adobe_AGM_Utils /AGMUTIL_ndx /AGMUTIL_ndx /load cvx 1 /add cvx /ddf cvx } for currentdict /dstStr get 0 /AGMUTIL_ndx /load cvx /getinterval cvx ] cvx /if cvx /end cvx ] cvx bind /exec cvx ] cvx end } bdf /AGMUTIL_imagefile nd /read_image_file { AGMUTIL_imagefile 0 setfileposition 10 dict begin /imageDict xdf /imbufLen Width BitsPerComponent mul 7 add 8 idiv def /imbufIdx 0 def /origDataSource imageDict /DataSource get def /origMultipleDataSources imageDict /MultipleDataSources get def /origDecode imageDict /Decode get def /dstDataStr imageDict /Width get colorSpaceElemCnt mul string def imageDict /MultipleDataSources known {MultipleDataSources}{false} ifelse { /imbufCnt imageDict /DataSource get length def /imbufs imbufCnt array def 0 1 imbufCnt 1 sub { /imbufIdx xdf imbufs imbufIdx imbufLen string put imageDict /DataSource get imbufIdx [ AGMUTIL_imagefile imbufs imbufIdx get /readstring cvx /pop cvx ] cvx put } for DeviceN_PS2 { imageDict begin /DataSource [ DataSource /devn_sep_datasource cvx ] cvx def /MultipleDataSources false def /Decode [0 1] def end } if }{ /imbuf imbufLen string def Indexed_DeviceN level3 not and DeviceN_NoneName or { /srcDataStrs [ imageDict begin currentdict /MultipleDataSources known {MultipleDataSources {DataSource length}{1}ifelse}{1} ifelse { Width Decode length 2 div mul cvi string } repeat end ] def imageDict begin /DataSource [AGMUTIL_imagefile Decode BitsPerComponent false 1 /filter_indexed_devn load dstDataStr srcDataStrs devn_alt_datasource /exec cvx] cvx def /Decode [0 1] def end }{ imageDict /DataSource [1 string dup 0 AGMUTIL_imagefile Decode length 2 idiv string/readstring cvx /pop cvx names_index /get cvx /put cvx] cvx put imageDict /Decode [0 1] put } ifelse } ifelse imageDict exch load exec imageDict /DataSource origDataSource put imageDict /MultipleDataSources origMultipleDataSources put imageDict /Decode origDecode put end } bdf /write_image_file { begin { (AGMUTIL_imagefile) (w+) file } stopped{ false }{ Adobe_AGM_Utils/AGMUTIL_imagefile xddf 2 dict begin /imbufLen Width BitsPerComponent mul 7 add 8 idiv def MultipleDataSources {DataSource 0 get}{DataSource}ifelse type /filetype eq { /imbuf imbufLen string def }if 1 1 Height MultipleDataSources not{Decode length 2 idiv mul}if{ pop MultipleDataSources { 0 1 DataSource length 1 sub { DataSource type dup /arraytype eq { pop DataSource exch get exec }{ /filetype eq { DataSource exch get imbuf readstring pop }{ DataSource exch get } ifelse } ifelse AGMUTIL_imagefile exch writestring } for }{ DataSource type dup /arraytype eq { pop DataSource exec }{ /filetype eq { DataSource imbuf readstring pop }{ DataSource } ifelse } ifelse AGMUTIL_imagefile exch writestring } ifelse }for end true }ifelse end } bdf /close_image_file { AGMUTIL_imagefile closefile (AGMUTIL_imagefile) deletefile }def statusdict /product known userdict /AGMP_current_show known not and{ /pstr statusdict /product get def pstr (HP LaserJet 2200) eq pstr (HP LaserJet 4000 Series) eq or pstr (HP LaserJet 4050 Series ) eq or pstr (HP LaserJet 8000 Series) eq or pstr (HP LaserJet 8100 Series) eq or pstr (HP LaserJet 8150 Series) eq or pstr (HP LaserJet 5000 Series) eq or pstr (HP LaserJet 5100 Series) eq or pstr (HP Color LaserJet 4500) eq or pstr (HP Color LaserJet 4600) eq or pstr (HP LaserJet 5Si) eq or pstr (HP LaserJet 1200 Series) eq or pstr (HP LaserJet 1300 Series) eq or pstr (HP LaserJet 4100 Series) eq or { userdict /AGMP_current_show /show load put userdict /show { currentcolorspace 0 get /Pattern eq {false charpath f} {AGMP_current_show} ifelse } put }if currentdict /pstr undef } if /consumeimagedata { begin currentdict /MultipleDataSources known not {/MultipleDataSources false def} if MultipleDataSources { DataSource 0 get type dup /filetype eq { 1 dict begin /flushbuffer Width cvi string def 1 1 Height cvi { pop 0 1 DataSource length 1 sub { DataSource exch get flushbuffer readstring pop pop }for }for end }if dup /arraytype eq exch /packedarraytype eq or DataSource 0 get xcheck and { Width Height mul cvi { 0 1 DataSource length 1 sub {dup DataSource exch get exec length exch 0 ne {pop}if}for dup 0 eq {pop exit}if sub dup 0 le {exit}if }loop pop }if } { /DataSource load type dup /filetype eq { 1 dict begin /flushbuffer Width Decode length 2 idiv mul cvi string def 1 1 Height { pop DataSource flushbuffer readstring pop pop} for end }if dup /arraytype eq exch /packedarraytype eq or /DataSource load xcheck and { Height Width BitsPerComponent mul 8 BitsPerComponent sub add 8 idiv Decode length 2 idiv mul mul { DataSource length dup 0 eq {pop exit}if sub dup 0 le {exit}if }loop pop }if }ifelse end }bdf /addprocs { 2{/exec load}repeat 3 1 roll [ 5 1 roll ] bind cvx }def /modify_halftone_xfer { currenthalftone dup length dict copy begin currentdict 2 index known{ 1 index load dup length dict copy begin currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end def currentdict end sethalftone }{ currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end sethalftone pop }ifelse }def /clonearray { dup xcheck exch dup length array exch Adobe_AGM_Core/AGMCORE_tmp -1 ddf { Adobe_AGM_Core/AGMCORE_tmp 2 copy get 1 add ddf dup type /dicttype eq { Adobe_AGM_Core/AGMCORE_tmp get exch clonedict Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf } if dup type /arraytype eq { Adobe_AGM_Core/AGMCORE_tmp get exch clonearray Adobe_AGM_Core/AGMCORE_tmp 4 -1 roll ddf } if exch dup Adobe_AGM_Core/AGMCORE_tmp get 4 -1 roll put }forall exch {cvx} if }bdf /clonedict { dup length dict begin { dup type /dicttype eq { clonedict } if dup type /arraytype eq { clonearray } if def }forall currentdict end }bdf /DeviceN_PS2 { /currentcolorspace AGMCORE_gget 0 get /DeviceN eq level3 not and } bdf /Indexed_DeviceN { /indexed_colorspace_dict AGMCORE_gget dup null ne { dup /CSDBase known { /CSDBase get /CSD get_res /Names known }{ pop false }ifelse }{ pop false } ifelse } bdf /DeviceN_NoneName { /Names where { pop false Names { (None) eq or } forall }{ false }ifelse } bdf /DeviceN_PS2_inRip_seps { /AGMCORE_in_rip_sep where { pop dup type dup /arraytype eq exch /packedarraytype eq or { dup 0 get /DeviceN eq level3 not and AGMCORE_in_rip_sep and { /currentcolorspace exch AGMCORE_gput false } { true }ifelse } { true } ifelse } { true } ifelse } bdf /base_colorspace_type { dup type /arraytype eq {0 get} if } bdf /currentdistillerparams where { pop currentdistillerparams /CoreDistVersion get 5000 lt}{true}ifelse { /pdfmark_5 {cleartomark} bind def }{ /pdfmark_5 {pdfmark} bind def }ifelse /ReadBypdfmark_5 { 2 dict begin /makerString exch def string /tmpString exch def { currentfile tmpString readline pop makerString anchorsearch { pop pop cleartomark exit }{ 3 copy /PUT pdfmark_5 pop 2 copy (\n) /PUT pdfmark_5 } ifelse }loop end } bdf /doc_setup{ Adobe_AGM_Utils begin }bdf /doc_trailer{ currentdict Adobe_AGM_Utils eq{ end }if }bdf systemdict /setpacking known { setpacking } if %%EndResource %%BeginResource: procset Adobe_AGM_Core 2.0 0 %%Version: 2.0 0 %%Copyright: Copyright (C) 1997-2005 Adobe Systems, Inc. All Rights Reserved. %% Note: This procset assumes Adobe_AGM_Utils is opened on the stack below it, for %% definitions of some fundamental procedures. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Core 201 dict dup begin put /Adobe_AGM_Core_Id /Adobe_AGM_Core_2.0_0 def /AGMCORE_str256 256 string def /AGMCORE_save nd /AGMCORE_graphicsave nd /AGMCORE_c 0 def /AGMCORE_m 0 def /AGMCORE_y 0 def /AGMCORE_k 0 def /AGMCORE_cmykbuf 4 array def /AGMCORE_screen [currentscreen] cvx def /AGMCORE_tmp 0 def /AGMCORE_&setgray nd /AGMCORE_&setcolor nd /AGMCORE_&setcolorspace nd /AGMCORE_&setcmykcolor nd /AGMCORE_cyan_plate nd /AGMCORE_magenta_plate nd /AGMCORE_yellow_plate nd /AGMCORE_black_plate nd /AGMCORE_plate_ndx nd /AGMCORE_get_ink_data nd /AGMCORE_is_cmyk_sep nd /AGMCORE_host_sep nd /AGMCORE_avoid_L2_sep_space nd /AGMCORE_distilling nd /AGMCORE_composite_job nd /AGMCORE_producing_seps nd /AGMCORE_ps_level -1 def /AGMCORE_ps_version -1 def /AGMCORE_environ_ok nd /AGMCORE_CSD_cache 0 dict def /AGMCORE_currentoverprint false def /AGMCORE_deltaX nd /AGMCORE_deltaY nd /AGMCORE_name nd /AGMCORE_sep_special nd /AGMCORE_err_strings 4 dict def /AGMCORE_cur_err nd /AGMCORE_current_spot_alias false def /AGMCORE_inverting false def /AGMCORE_feature_dictCount nd /AGMCORE_feature_opCount nd /AGMCORE_feature_ctm nd /AGMCORE_ConvertToProcess false def /AGMCORE_Default_CTM matrix def /AGMCORE_Default_PageSize nd /AGMCORE_currentbg nd /AGMCORE_currentucr nd /AGMCORE_in_pattern false def /AGMCORE_currentpagedevice nd /knockout_unitsq nd currentglobal true setglobal [/CSA /Gradient /Procedure] { /Generic /Category findresource dup length dict copy /Category defineresource pop } forall setglobal /AGMCORE_key_known { where{ /Adobe_AGM_Core_Id known }{ false }ifelse }ndf /flushinput { save 2 dict begin /CompareBuffer 3 -1 roll def /readbuffer 256 string def mark { currentfile readbuffer {readline} stopped {cleartomark mark} { not {pop exit} if CompareBuffer eq {exit} if }ifelse }loop cleartomark end restore }bdf /getspotfunction { AGMCORE_screen exch pop exch pop dup type /dicttype eq{ dup /HalftoneType get 1 eq{ /SpotFunction get }{ dup /HalftoneType get 2 eq{ /GraySpotFunction get }{ pop { abs exch abs 2 copy add 1 gt{ 1 sub dup mul exch 1 sub dup mul add 1 sub }{ dup mul exch dup mul add 1 exch sub }ifelse }bind }ifelse }ifelse }if } def /clp_npth { clip newpath } def /eoclp_npth { eoclip newpath } def /npth_clp { newpath clip } def /graphic_setup { /AGMCORE_graphicsave save def concat 0 setgray 0 setlinecap 0 setlinejoin 1 setlinewidth [] 0 setdash 10 setmiterlimit newpath false setoverprint false setstrokeadjust //Adobe_AGM_Core/spot_alias get exec /Adobe_AGM_Image where { pop Adobe_AGM_Image/spot_alias 2 copy known{ get exec }{ pop pop }ifelse } if 100 dict begin /dictstackcount countdictstack def /showpage {} def mark } def /graphic_cleanup { cleartomark dictstackcount 1 countdictstack 1 sub {end}for end AGMCORE_graphicsave restore } def /compose_error_msg { grestoreall initgraphics /Helvetica findfont 10 scalefont setfont /AGMCORE_deltaY 100 def /AGMCORE_deltaX 310 def clippath pathbbox newpath pop pop 36 add exch 36 add exch moveto 0 AGMCORE_deltaY rlineto AGMCORE_deltaX 0 rlineto 0 AGMCORE_deltaY neg rlineto AGMCORE_deltaX neg 0 rlineto closepath 0 AGMCORE_&setgray gsave 1 AGMCORE_&setgray fill grestore 1 setlinewidth gsave stroke grestore currentpoint AGMCORE_deltaY 15 sub add exch 8 add exch moveto /AGMCORE_deltaY 12 def /AGMCORE_tmp 0 def AGMCORE_err_strings exch get { dup 32 eq { pop AGMCORE_str256 0 AGMCORE_tmp getinterval stringwidth pop currentpoint pop add AGMCORE_deltaX 28 add gt { currentpoint AGMCORE_deltaY sub exch pop clippath pathbbox pop pop pop 44 add exch moveto } if AGMCORE_str256 0 AGMCORE_tmp getinterval show ( ) show 0 1 AGMCORE_str256 length 1 sub { AGMCORE_str256 exch 0 put }for /AGMCORE_tmp 0 def } { AGMCORE_str256 exch AGMCORE_tmp xpt /AGMCORE_tmp AGMCORE_tmp 1 add def } ifelse } forall } bdf /doc_setup{ Adobe_AGM_Core begin /AGMCORE_ps_version xdf /AGMCORE_ps_level xdf errordict /AGM_handleerror known not{ errordict /AGM_handleerror errordict /handleerror get put errordict /handleerror { Adobe_AGM_Core begin $error /newerror get AGMCORE_cur_err null ne and{ $error /newerror false put AGMCORE_cur_err compose_error_msg }if $error /newerror true put end errordict /AGM_handleerror get exec } bind put }if /AGMCORE_environ_ok ps_level AGMCORE_ps_level ge ps_version AGMCORE_ps_version ge and AGMCORE_ps_level -1 eq or def AGMCORE_environ_ok not {/AGMCORE_cur_err /AGMCORE_bad_environ def} if /AGMCORE_&setgray systemdict/setgray get def level2{ /AGMCORE_&setcolor systemdict/setcolor get def /AGMCORE_&setcolorspace systemdict/setcolorspace get def }if /AGMCORE_currentbg currentblackgeneration def /AGMCORE_currentucr currentundercolorremoval def /AGMCORE_distilling /product where{ pop systemdict/setdistillerparams known product (Adobe PostScript Parser) ne and }{ false }ifelse def /AGMCORE_GSTATE AGMCORE_key_known not{ /AGMCORE_GSTATE 21 dict def /AGMCORE_tmpmatrix matrix def /AGMCORE_gstack 32 array def /AGMCORE_gstackptr 0 def /AGMCORE_gstacksaveptr 0 def /AGMCORE_gstackframekeys 10 def /AGMCORE_&gsave /gsave ldf /AGMCORE_&grestore /grestore ldf /AGMCORE_&grestoreall /grestoreall ldf /AGMCORE_&save /save ldf /AGMCORE_&setoverprint /setoverprint ldf /AGMCORE_gdictcopy { begin { def } forall end }def /AGMCORE_gput { AGMCORE_gstack AGMCORE_gstackptr get 3 1 roll put }def /AGMCORE_gget { AGMCORE_gstack AGMCORE_gstackptr get exch get }def /gsave { AGMCORE_&gsave AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if /AGMCORE_gstackptr exch store AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def /grestore { AGMCORE_&grestore AGMCORE_gstackptr 1 sub dup AGMCORE_gstacksaveptr lt {1 add} if dup AGMCORE_gstack exch get dup /AGMCORE_currentoverprint known {/AGMCORE_currentoverprint get setoverprint}{pop}ifelse /AGMCORE_gstackptr exch store }def /grestoreall { AGMCORE_&grestoreall /AGMCORE_gstackptr AGMCORE_gstacksaveptr store }def /save { AGMCORE_&save AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if /AGMCORE_gstackptr exch store /AGMCORE_gstacksaveptr AGMCORE_gstackptr store AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def /setoverprint{ dup /AGMCORE_currentoverprint exch AGMCORE_gput AGMCORE_&setoverprint }def 0 1 AGMCORE_gstack length 1 sub { AGMCORE_gstack exch AGMCORE_gstackframekeys dict put } for }if level3 /AGMCORE_&sysshfill AGMCORE_key_known not and { /AGMCORE_&sysshfill systemdict/shfill get def /AGMCORE_&sysmakepattern systemdict/makepattern get def /AGMCORE_&usrmakepattern /makepattern load def }if /currentcmykcolor [0 0 0 0] AGMCORE_gput /currentstrokeadjust false AGMCORE_gput /currentcolorspace [/DeviceGray] AGMCORE_gput /sep_tint 0 AGMCORE_gput /devicen_tints [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] AGMCORE_gput /sep_colorspace_dict null AGMCORE_gput /devicen_colorspace_dict null AGMCORE_gput /indexed_colorspace_dict null AGMCORE_gput /currentcolor_intent () AGMCORE_gput /customcolor_tint 1 AGMCORE_gput << /MaxPatternItem currentsystemparams /MaxPatternCache get >> setuserparams end }def /page_setup { /setcmykcolor where{ pop Adobe_AGM_Core/AGMCORE_&setcmykcolor /setcmykcolor load put }if Adobe_AGM_Core begin /setcmykcolor { 4 copy AGMCORE_cmykbuf astore /currentcmykcolor exch AGMCORE_gput 1 sub 4 1 roll 3 { 3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat setrgbcolor pop }ndf /currentcmykcolor { /currentcmykcolor AGMCORE_gget aload pop }ndf /setoverprint { pop }ndf /currentoverprint { false }ndf /AGMCORE_cyan_plate 1 0 0 0 test_cmyk_color_plate def /AGMCORE_magenta_plate 0 1 0 0 test_cmyk_color_plate def /AGMCORE_yellow_plate 0 0 1 0 test_cmyk_color_plate def /AGMCORE_black_plate 0 0 0 1 test_cmyk_color_plate def /AGMCORE_plate_ndx AGMCORE_cyan_plate{ 0 }{ AGMCORE_magenta_plate{ 1 }{ AGMCORE_yellow_plate{ 2 }{ AGMCORE_black_plate{ 3 }{ 4 }ifelse }ifelse }ifelse }ifelse def /AGMCORE_have_reported_unsupported_color_space false def /AGMCORE_report_unsupported_color_space { AGMCORE_have_reported_unsupported_color_space false eq { (Warning: Job contains content that cannot be separated with on-host methods. This content appears on the black plate, and knocks out all other plates.) == Adobe_AGM_Core /AGMCORE_have_reported_unsupported_color_space true ddf } if }def /AGMCORE_composite_job AGMCORE_cyan_plate AGMCORE_magenta_plate and AGMCORE_yellow_plate and AGMCORE_black_plate and def /AGMCORE_in_rip_sep /AGMCORE_in_rip_sep where{ pop AGMCORE_in_rip_sep }{ AGMCORE_distilling { false }{ userdict/Adobe_AGM_OnHost_Seps known{ false }{ level2{ currentpagedevice/Separations 2 copy known{ get }{ pop pop false }ifelse }{ false }ifelse }ifelse }ifelse }ifelse def /AGMCORE_producing_seps AGMCORE_composite_job not AGMCORE_in_rip_sep or def /AGMCORE_host_sep AGMCORE_producing_seps AGMCORE_in_rip_sep not and def /AGM_preserve_spots /AGM_preserve_spots where{ pop AGM_preserve_spots }{ AGMCORE_distilling AGMCORE_producing_seps or }ifelse def /AGM_is_distiller_preserving_spotimages { currentdistillerparams/PreserveOverprintSettings known { currentdistillerparams/PreserveOverprintSettings get { currentdistillerparams/ColorConversionStrategy known { currentdistillerparams/ColorConversionStrategy get /sRGB ne }{ true }ifelse }{ false }ifelse }{ false }ifelse }def /convert_spot_to_process where {pop}{ /convert_spot_to_process { //Adobe_AGM_Core begin dup map_alias { /Name get exch pop } if dup dup (None) eq exch (All) eq or { pop false }{ AGMCORE_host_sep { gsave 1 0 0 0 setcmykcolor currentgray 1 exch sub 0 1 0 0 setcmykcolor currentgray 1 exch sub 0 0 1 0 setcmykcolor currentgray 1 exch sub 0 0 0 1 setcmykcolor currentgray 1 exch sub add add add 0 eq { pop false }{ false setoverprint current_spot_alias false set_spot_alias 1 1 1 1 6 -1 roll findcmykcustomcolor 1 setcustomcolor set_spot_alias currentgray 1 ne }ifelse grestore }{ AGMCORE_distilling { pop AGM_is_distiller_preserving_spotimages not }{ //Adobe_AGM_Core/AGMCORE_name xddf false //Adobe_AGM_Core/AGMCORE_in_pattern known {//Adobe_AGM_Core/AGMCORE_in_pattern get}{false} ifelse not AGMCORE_currentpagedevice/OverrideSeparations known and { AGMCORE_currentpagedevice/OverrideSeparations get { /HqnSpots /ProcSet resourcestatus { pop pop pop true }if }if }if { AGMCORE_name /HqnSpots /ProcSet findresource /TestSpot get exec not }{ gsave [/Separation AGMCORE_name /DeviceGray {}]AGMCORE_&setcolorspace false AGMCORE_currentpagedevice/SeparationColorNames 2 copy known { get { AGMCORE_name eq or}forall not }{ pop pop pop true }ifelse grestore }ifelse }ifelse }ifelse }ifelse end }def }ifelse /convert_to_process where {pop}{ /convert_to_process { dup length 0 eq { pop false }{ AGMCORE_host_sep { dup true exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch dup (Black) eq 3 -1 roll or {pop} {convert_spot_to_process and}ifelse } forall { true exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch (Black) eq or and }forall not }{pop false}ifelse }{ false exch { dup (Cyan) eq exch dup (Magenta) eq 3 -1 roll or exch dup (Yellow) eq 3 -1 roll or exch dup (Black) eq 3 -1 roll or {pop} {convert_spot_to_process or}ifelse } forall }ifelse }ifelse }def }ifelse /AGMCORE_avoid_L2_sep_space version cvr 2012 lt level2 and AGMCORE_producing_seps not and def /AGMCORE_is_cmyk_sep AGMCORE_cyan_plate AGMCORE_magenta_plate or AGMCORE_yellow_plate or AGMCORE_black_plate or def /AGM_avoid_0_cmyk where{ pop AGM_avoid_0_cmyk }{ AGM_preserve_spots userdict/Adobe_AGM_OnHost_Seps known userdict/Adobe_AGM_InRip_Seps known or not and }ifelse { /setcmykcolor[ { 4 copy add add add 0 eq currentoverprint and{ pop 0.0005 }if }/exec cvx /AGMCORE_&setcmykcolor load dup type/operatortype ne{ /exec cvx }if ]cvx def }if /AGMCORE_IsSeparationAProcessColor { dup (Cyan) eq exch dup (Magenta) eq exch dup (Yellow) eq exch (Black) eq or or or }def AGMCORE_host_sep{ /setcolortransfer { AGMCORE_cyan_plate{ pop pop pop }{ AGMCORE_magenta_plate{ 4 3 roll pop pop pop }{ AGMCORE_yellow_plate{ 4 2 roll pop pop pop }{ 4 1 roll pop pop pop }ifelse }ifelse }ifelse settransfer } def /AGMCORE_get_ink_data AGMCORE_cyan_plate{ {pop pop pop} }{ AGMCORE_magenta_plate{ {4 3 roll pop pop pop} }{ AGMCORE_yellow_plate{ {4 2 roll pop pop pop} }{ {4 1 roll pop pop pop} }ifelse }ifelse }ifelse def /AGMCORE_RemoveProcessColorNames { 1 dict begin /filtername { dup /Cyan eq 1 index (Cyan) eq or {pop (_cyan_)}if dup /Magenta eq 1 index (Magenta) eq or {pop (_magenta_)}if dup /Yellow eq 1 index (Yellow) eq or {pop (_yellow_)}if dup /Black eq 1 index (Black) eq or {pop (_black_)}if }def dup type /arraytype eq {[exch {filtername}forall]} {filtername}ifelse end }def level3 { /AGMCORE_IsCurrentColor { dup AGMCORE_IsSeparationAProcessColor { AGMCORE_plate_ndx 0 eq {dup (Cyan) eq exch /Cyan eq or}if AGMCORE_plate_ndx 1 eq {dup (Magenta) eq exch /Magenta eq or}if AGMCORE_plate_ndx 2 eq {dup (Yellow) eq exch /Yellow eq or}if AGMCORE_plate_ndx 3 eq {dup (Black) eq exch /Black eq or}if AGMCORE_plate_ndx 4 eq {pop false}if }{ gsave false setoverprint current_spot_alias false set_spot_alias 1 1 1 1 6 -1 roll findcmykcustomcolor 1 setcustomcolor set_spot_alias currentgray 1 ne grestore }ifelse }def /AGMCORE_filter_functiondatasource { 5 dict begin /data_in xdf data_in type /stringtype eq { /ncomp xdf /comp xdf /string_out data_in length ncomp idiv string def 0 ncomp data_in length 1 sub { string_out exch dup ncomp idiv exch data_in exch ncomp getinterval comp get 255 exch sub put }for string_out }{ string /string_in xdf /string_out 1 string def /component xdf [ data_in string_in /readstring cvx [component /get cvx 255 /exch cvx /sub cvx string_out /exch cvx 0 /exch cvx /put cvx string_out]cvx [/pop cvx ()]cvx /ifelse cvx ]cvx /ReusableStreamDecode filter }ifelse end }def /AGMCORE_separateShadingFunction { 2 dict begin /paint? xdf /channel xdf dup type /dicttype eq { begin FunctionType 0 eq { /DataSource channel Range length 2 idiv DataSource AGMCORE_filter_functiondatasource def currentdict /Decode known {/Decode Decode channel 2 mul 2 getinterval def}if paint? not {/Decode [1 1]def}if }if FunctionType 2 eq { paint? { /C0 [C0 channel get 1 exch sub] def /C1 [C1 channel get 1 exch sub] def }{ /C0 [1] def /C1 [1] def }ifelse }if FunctionType 3 eq { /Functions [Functions {channel paint? AGMCORE_separateShadingFunction} forall] def }if currentdict /Range known {/Range [0 1] def}if currentdict end}{ channel get 0 paint? AGMCORE_separateShadingFunction }ifelse end }def /AGMCORE_separateShading { 3 -1 roll begin currentdict /Function known { currentdict /Background known {[1 index{Background 3 index get 1 exch sub}{1}ifelse]/Background xdf}if Function 3 1 roll AGMCORE_separateShadingFunction /Function xdf /ColorSpace [/DeviceGray] def }{ ColorSpace dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [/DeviceN [/_cyan_ /_magenta_ /_yellow_ /_black_] /DeviceCMYK {}] def }{ ColorSpace dup 1 get AGMCORE_RemoveProcessColorNames 1 exch put }ifelse ColorSpace 0 get /Separation eq { { [1 /exch cvx /sub cvx]cvx }{ [/pop cvx 1]cvx }ifelse ColorSpace 3 3 -1 roll put pop }{ { [exch ColorSpace 1 get length 1 sub exch sub /index cvx 1 /exch cvx /sub cvx ColorSpace 1 get length 1 add 1 /roll cvx ColorSpace 1 get length{/pop cvx} repeat]cvx }{ pop [ColorSpace 1 get length {/pop cvx} repeat cvx 1]cvx }ifelse ColorSpace 3 3 -1 roll bind put }ifelse ColorSpace 2 /DeviceGray put }ifelse end }def /AGMCORE_separateShadingDict { dup /ColorSpace get dup type /arraytype ne {[exch]}if dup 0 get /DeviceCMYK eq { exch begin currentdict AGMCORE_cyan_plate {0 true}if AGMCORE_magenta_plate {1 true}if AGMCORE_yellow_plate {2 true}if AGMCORE_black_plate {3 true}if AGMCORE_plate_ndx 4 eq {0 false}if dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading currentdict end exch }if dup 0 get /Separation eq { exch begin ColorSpace 1 get dup /None ne exch /All ne and { ColorSpace 1 get AGMCORE_IsCurrentColor AGMCORE_plate_ndx 4 lt and ColorSpace 1 get AGMCORE_IsSeparationAProcessColor not and { ColorSpace 2 get dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [ /Separation ColorSpace 1 get /DeviceGray [ ColorSpace 3 get /exec cvx 4 AGMCORE_plate_ndx sub -1 /roll cvx 4 1 /roll cvx 3 [/pop cvx]cvx /repeat cvx 1 /exch cvx /sub cvx ]cvx ]def }{ AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { currentdict 0 false AGMCORE_separateShading }if }ifelse }{ currentdict ColorSpace 1 get AGMCORE_IsCurrentColor 0 exch dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading }ifelse }if currentdict end exch }if dup 0 get /DeviceN eq { exch begin ColorSpace 1 get convert_to_process { ColorSpace 2 get dup type /arraytype eq {0 get}if /DeviceCMYK eq { /ColorSpace [ /DeviceN ColorSpace 1 get /DeviceGray [ ColorSpace 3 get /exec cvx 4 AGMCORE_plate_ndx sub -1 /roll cvx 4 1 /roll cvx 3 [/pop cvx]cvx /repeat cvx 1 /exch cvx /sub cvx ]cvx ]def }{ AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { currentdict 0 false AGMCORE_separateShading /ColorSpace [/DeviceGray] def }if }ifelse }{ currentdict false -1 ColorSpace 1 get { AGMCORE_IsCurrentColor { 1 add exch pop true exch exit }if 1 add }forall exch dup not currentoverprint and {/AGMCORE_ignoreshade true def}if AGMCORE_separateShading }ifelse currentdict end exch }if dup 0 get dup /DeviceCMYK eq exch dup /Separation eq exch /DeviceN eq or or not { exch begin ColorSpace dup type /arraytype eq {0 get}if /DeviceGray ne { AGMCORE_report_unsupported_color_space AGMCORE_black_plate not { ColorSpace 0 get /CIEBasedA eq { /ColorSpace [/Separation /_ciebaseda_ /DeviceGray {}] def }if ColorSpace 0 get dup /CIEBasedABC eq exch dup /CIEBasedDEF eq exch /DeviceRGB eq or or { /ColorSpace [/DeviceN [/_red_ /_green_ /_blue_] /DeviceRGB {}] def }if ColorSpace 0 get /CIEBasedDEFG eq { /ColorSpace [/DeviceN [/_cyan_ /_magenta_ /_yellow_ /_black_] /DeviceCMYK {}] def }if currentdict 0 false AGMCORE_separateShading }if }if currentdict end exch }if pop dup /AGMCORE_ignoreshade known { begin /ColorSpace [/Separation (None) /DeviceGray {}] def currentdict end }if }def /shfill { AGMCORE_separateShadingDict dup /AGMCORE_ignoreshade known {pop} {AGMCORE_&sysshfill}ifelse }def /makepattern { exch dup /PatternType get 2 eq { clonedict begin /Shading Shading AGMCORE_separateShadingDict def Shading /AGMCORE_ignoreshade known currentdict end exch {pop <>}if exch AGMCORE_&sysmakepattern }{ exch AGMCORE_&usrmakepattern }ifelse }def }if }if AGMCORE_in_rip_sep{ /setcustomcolor { exch aload pop dup 7 1 roll inRip_spot_has_ink not { 4 {4 index mul 4 1 roll} repeat /DeviceCMYK setcolorspace 6 -2 roll pop pop }{ //Adobe_AGM_Core begin /AGMCORE_k xdf /AGMCORE_y xdf /AGMCORE_m xdf /AGMCORE_c xdf end [/Separation 4 -1 roll /DeviceCMYK {dup AGMCORE_c mul exch dup AGMCORE_m mul exch dup AGMCORE_y mul exch AGMCORE_k mul} ] setcolorspace }ifelse setcolor }ndf /setseparationgray { [/Separation (All) /DeviceGray {}] setcolorspace_opt 1 exch sub setcolor }ndf }{ /setseparationgray { AGMCORE_&setgray }ndf }ifelse /findcmykcustomcolor { 5 makereadonlyarray }ndf /setcustomcolor { exch aload pop pop 4 {4 index mul 4 1 roll} repeat setcmykcolor pop }ndf /has_color /colorimage where{ AGMCORE_producing_seps{ pop true }{ systemdict eq }ifelse }{ false }ifelse def /map_index { 1 index mul exch getinterval {255 div} forall } bdf /map_indexed_devn { Lookup Names length 3 -1 roll cvi map_index } bdf /n_color_components { base_colorspace_type dup /DeviceGray eq{ pop 1 }{ /DeviceCMYK eq{ 4 }{ 3 }ifelse }ifelse }bdf level2{ /mo /moveto ldf /li /lineto ldf /cv /curveto ldf /knockout_unitsq { 1 setgray 0 0 1 1 rectfill }def level2 /setcolorspace AGMCORE_key_known not and{ /AGMCORE_&&&setcolorspace /setcolorspace ldf /AGMCORE_ReplaceMappedColor { dup type dup /arraytype eq exch /packedarraytype eq or { /AGMCORE_SpotAliasAry2 where { begin dup 0 get dup /Separation eq { pop dup length array copy dup dup 1 get current_spot_alias { dup map_alias { false set_spot_alias dup 1 exch setsepcolorspace true set_spot_alias begin /sep_colorspace_dict currentdict AGMCORE_gput pop pop pop [ /Separation Name CSA map_csa MappedCSA /sep_colorspace_proc load ] dup Name end }if }if map_reserved_ink_name 1 xpt }{ /DeviceN eq { dup length array copy dup dup 1 get [ exch { current_spot_alias{ dup map_alias{ /Name get exch pop }if }if map_reserved_ink_name } forall ] 1 xpt }if }ifelse end } if }if }def /setcolorspace { dup type dup /arraytype eq exch /packedarraytype eq or { dup 0 get /Indexed eq { AGMCORE_distilling { /PhotoshopDuotoneList where { pop false }{ true }ifelse }{ true }ifelse { aload pop 3 -1 roll AGMCORE_ReplaceMappedColor 3 1 roll 4 array astore }if }{ AGMCORE_ReplaceMappedColor }ifelse }if DeviceN_PS2_inRip_seps {AGMCORE_&&&setcolorspace} if }def }if }{ /adj { currentstrokeadjust{ transform 0.25 sub round 0.25 add exch 0.25 sub round 0.25 add exch itransform }if }def /mo{ adj moveto }def /li{ adj lineto }def /cv{ 6 2 roll adj 6 2 roll adj 6 2 roll adj curveto }def /knockout_unitsq { 1 setgray 8 8 1 [8 0 0 8 0 0] {} image }def /currentstrokeadjust{ /currentstrokeadjust AGMCORE_gget }def /setstrokeadjust{ /currentstrokeadjust exch AGMCORE_gput }def /setcolorspace { /currentcolorspace exch AGMCORE_gput } def /currentcolorspace { /currentcolorspace AGMCORE_gget } def /setcolor_devicecolor { base_colorspace_type dup /DeviceGray eq{ pop setgray }{ /DeviceCMYK eq{ setcmykcolor }{ setrgbcolor }ifelse }ifelse }def /setcolor { currentcolorspace 0 get dup /DeviceGray ne{ dup /DeviceCMYK ne{ dup /DeviceRGB ne{ dup /Separation eq{ pop currentcolorspace 3 get exec currentcolorspace 2 get }{ dup /Indexed eq{ pop currentcolorspace 3 get dup type /stringtype eq{ currentcolorspace 1 get n_color_components 3 -1 roll map_index }{ exec }ifelse currentcolorspace 1 get }{ /AGMCORE_cur_err /AGMCORE_invalid_color_space def AGMCORE_invalid_color_space }ifelse }ifelse }if }if }if setcolor_devicecolor } def }ifelse /sop /setoverprint ldf /lw /setlinewidth ldf /lc /setlinecap ldf /lj /setlinejoin ldf /ml /setmiterlimit ldf /dsh /setdash ldf /sadj /setstrokeadjust ldf /gry /setgray ldf /rgb /setrgbcolor ldf /cmyk /setcmykcolor ldf /sep /setsepcolor ldf /devn /setdevicencolor ldf /idx /setindexedcolor ldf /colr /setcolor ldf /csacrd /set_csa_crd ldf /sepcs /setsepcolorspace ldf /devncs /setdevicencolorspace ldf /idxcs /setindexedcolorspace ldf /cp /closepath ldf /clp /clp_npth ldf /eclp /eoclp_npth ldf /f /fill ldf /ef /eofill ldf /@ /stroke ldf /nclp /npth_clp ldf /gset /graphic_setup ldf /gcln /graphic_cleanup ldf /AGMCORE_def_ht currenthalftone def /clonedict Adobe_AGM_Utils begin /clonedict load end def /clonearray Adobe_AGM_Utils begin /clonearray load end def currentdict{ dup xcheck 1 index type dup /arraytype eq exch /packedarraytype eq or and { bind }if def }forall /getrampcolor { /indx exch def 0 1 NumComp 1 sub { dup Samples exch get dup type /stringtype eq {indx get} if exch Scaling exch get aload pop 3 1 roll mul add } for ColorSpaceFamily /Separation eq {sep} { ColorSpaceFamily /DeviceN eq {devn} {setcolor}ifelse }ifelse } bdf /sssetbackground {aload pop setcolor} bdf /RadialShade { 40 dict begin /ColorSpaceFamily xdf /background xdf /ext1 xdf /ext0 xdf /BBox xdf /r2 xdf /c2y xdf /c2x xdf /r1 xdf /c1y xdf /c1x xdf /rampdict xdf /setinkoverprint where {pop /setinkoverprint{pop}def}if gsave BBox length 0 gt { newpath BBox 0 get BBox 1 get moveto BBox 2 get BBox 0 get sub 0 rlineto 0 BBox 3 get BBox 1 get sub rlineto BBox 2 get BBox 0 get sub neg 0 rlineto closepath clip newpath } if c1x c2x eq { c1y c2y lt {/theta 90 def}{/theta 270 def} ifelse } { /slope c2y c1y sub c2x c1x sub div def /theta slope 1 atan def c2x c1x lt c2y c1y ge and { /theta theta 180 sub def} if c2x c1x lt c2y c1y lt and { /theta theta 180 add def} if } ifelse gsave clippath c1x c1y translate theta rotate -90 rotate { pathbbox } stopped { 0 0 0 0 } if /yMax xdf /xMax xdf /yMin xdf /xMin xdf grestore xMax xMin eq yMax yMin eq or { grestore end } { /max { 2 copy gt { pop } {exch pop} ifelse } bdf /min { 2 copy lt { pop } {exch pop} ifelse } bdf rampdict begin 40 dict begin background length 0 gt { background sssetbackground gsave clippath fill grestore } if gsave c1x c1y translate theta rotate -90 rotate /c2y c1x c2x sub dup mul c1y c2y sub dup mul add sqrt def /c1y 0 def /c1x 0 def /c2x 0 def ext0 { 0 getrampcolor c2y r2 add r1 sub 0.0001 lt { c1x c1y r1 360 0 arcn pathbbox /aymax exch def /axmax exch def /aymin exch def /axmin exch def /bxMin xMin axmin min def /byMin yMin aymin min def /bxMax xMax axmax max def /byMax yMax aymax max def bxMin byMin moveto bxMax byMin lineto bxMax byMax lineto bxMin byMax lineto bxMin byMin lineto eofill } { c2y r1 add r2 le { c1x c1y r1 0 360 arc fill } { c2x c2y r2 0 360 arc fill r1 r2 eq { /p1x r1 neg def /p1y c1y def /p2x r1 def /p2y c1y def p1x p1y moveto p2x p2y lineto p2x yMin lineto p1x yMin lineto fill } { /AA r2 r1 sub c2y div def AA -1 eq { /theta 89.99 def} { /theta AA 1 AA dup mul sub sqrt div 1 atan def} ifelse /SS1 90 theta add dup sin exch cos div def /p1x r1 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def /p1y p1x SS1 div neg def /SS2 90 theta sub dup sin exch cos div def /p2x r1 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def /p2y p2x SS2 div neg def r1 r2 gt { /L1maxX p1x yMin p1y sub SS1 div add def /L2maxX p2x yMin p2y sub SS2 div add def } { /L1maxX 0 def /L2maxX 0 def } ifelse p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto L1maxX L1maxX p1x sub SS1 mul p1y add lineto fill } ifelse } ifelse } ifelse } if c1x c2x sub dup mul c1y c2y sub dup mul add 0.5 exp 0 dtransform dup mul exch dup mul add 0.5 exp 72 div 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 1 index 1 index lt { exch } if pop /hires xdf hires mul /numpix xdf /numsteps NumSamples def /rampIndxInc 1 def /subsampling false def numpix 0 ne { NumSamples numpix div 0.5 gt { /numsteps numpix 2 div round cvi dup 1 le { pop 2 } if def /rampIndxInc NumSamples 1 sub numsteps div def /subsampling true def } if } if /xInc c2x c1x sub numsteps div def /yInc c2y c1y sub numsteps div def /rInc r2 r1 sub numsteps div def /cx c1x def /cy c1y def /radius r1 def newpath xInc 0 eq yInc 0 eq rInc 0 eq and and { 0 getrampcolor cx cy radius 0 360 arc stroke NumSamples 1 sub getrampcolor cx cy radius 72 hires div add 0 360 arc 0 setlinewidth stroke } { 0 numsteps { dup subsampling { round cvi } if getrampcolor cx cy radius 0 360 arc /cx cx xInc add def /cy cy yInc add def /radius radius rInc add def cx cy radius 360 0 arcn eofill rampIndxInc add } repeat pop } ifelse ext1 { c2y r2 add r1 lt { c2x c2y r2 0 360 arc fill } { c2y r1 add r2 sub 0.0001 le { c2x c2y r2 360 0 arcn pathbbox /aymax exch def /axmax exch def /aymin exch def /axmin exch def /bxMin xMin axmin min def /byMin yMin aymin min def /bxMax xMax axmax max def /byMax yMax aymax max def bxMin byMin moveto bxMax byMin lineto bxMax byMax lineto bxMin byMax lineto bxMin byMin lineto eofill } { c2x c2y r2 0 360 arc fill r1 r2 eq { /p1x r2 neg def /p1y c2y def /p2x r2 def /p2y c2y def p1x p1y moveto p2x p2y lineto p2x yMax lineto p1x yMax lineto fill } { /AA r2 r1 sub c2y div def AA -1 eq { /theta 89.99 def} { /theta AA 1 AA dup mul sub sqrt div 1 atan def} ifelse /SS1 90 theta add dup sin exch cos div def /p1x r2 SS1 SS1 mul SS1 SS1 mul 1 add div sqrt mul neg def /p1y c2y p1x SS1 div sub def /SS2 90 theta sub dup sin exch cos div def /p2x r2 SS2 SS2 mul SS2 SS2 mul 1 add div sqrt mul def /p2y c2y p2x SS2 div sub def r1 r2 lt { /L1maxX p1x yMax p1y sub SS1 div add def /L2maxX p2x yMax p2y sub SS2 div add def } { /L1maxX 0 def /L2maxX 0 def }ifelse p1x p1y moveto p2x p2y lineto L2maxX L2maxX p2x sub SS2 mul p2y add lineto L1maxX L1maxX p1x sub SS1 mul p1y add lineto fill } ifelse } ifelse } ifelse } if grestore grestore end end end } ifelse } bdf /GenStrips { 40 dict begin /ColorSpaceFamily xdf /background xdf /ext1 xdf /ext0 xdf /BBox xdf /y2 xdf /x2 xdf /y1 xdf /x1 xdf /rampdict xdf /setinkoverprint where {pop /setinkoverprint{pop}def}if gsave BBox length 0 gt { newpath BBox 0 get BBox 1 get moveto BBox 2 get BBox 0 get sub 0 rlineto 0 BBox 3 get BBox 1 get sub rlineto BBox 2 get BBox 0 get sub neg 0 rlineto closepath clip newpath } if x1 x2 eq { y1 y2 lt {/theta 90 def}{/theta 270 def} ifelse } { /slope y2 y1 sub x2 x1 sub div def /theta slope 1 atan def x2 x1 lt y2 y1 ge and { /theta theta 180 sub def} if x2 x1 lt y2 y1 lt and { /theta theta 180 add def} if } ifelse gsave clippath x1 y1 translate theta rotate { pathbbox } stopped { 0 0 0 0 } if /yMax exch def /xMax exch def /yMin exch def /xMin exch def grestore xMax xMin eq yMax yMin eq or { grestore end } { rampdict begin 20 dict begin background length 0 gt { background sssetbackground gsave clippath fill grestore } if gsave x1 y1 translate theta rotate /xStart 0 def /xEnd x2 x1 sub dup mul y2 y1 sub dup mul add 0.5 exp def /ySpan yMax yMin sub def /numsteps NumSamples def /rampIndxInc 1 def /subsampling false def xStart 0 transform xEnd 0 transform 3 -1 roll sub dup mul 3 1 roll sub dup mul add 0.5 exp 72 div 0 72 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt- - - - application/postscript - - - Adobe Illustrator CS2 - 2007-10-08T19:08:06-04:00 - 2007-10-08T19:08:06-04:00 - 2007-10-08T19:08:06-04:00 - - - - 256 - 256 - JPEG - /9j/4AAQSkZJRgABAgEAyADIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAAyAAAAAEA AQDIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A4NiydirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVRFhYXuoXsNjY wvc3ly4jggjBZ3djQKoHUnFWT/8AKofzP/6lfUf+kd/6Yrbv+VQ/mf8A9SvqP/SO/wDTFbd/yqH8 z/8AqV9R/wCkd/6Yrbv+VQ/mf/1K+o/9I7/0xW3f8qh/M/8A6lfUf+kd/wCmK27/AJVD+Z//AFK+ o/8ASO/9MVt3/KofzP8A+pX1H/pHf+mK27/lUP5n/wDUr6j/ANI7/wBMVt3/ACqH8z/+pX1H/pHf +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 GhJllobu+loZ52HQuwA2FfhUbDFDKcVdir//2Q== - - - - - - uuid:A113817B775811DCAE9FE3BA41DA243B - uuid:A113817C775811DCAE9FE3BA41DA243B - - uuid:A113817A775811DCAE9FE3BA41DA243B - uuid:A1138179775811DCAE9FE3BA41DA243B - - - - - - - - - - - - - - - - - - - - - - - - - - % &&end XMP packet marker&& [{ai_metadata_stream_123} <> /PUT AI11_PDFMark5 [/Document 1 dict begin /Metadata {ai_metadata_stream_123} def currentdict end /BDC AI11_PDFMark5 %AI12_RMC_Transparency: Balance=75 RasterRes=300 GradRes=150 Text=0 Stroke=1 Clip=1 OP=0 Adobe_AGM_Utils begin Adobe_AGM_Core/page_setup get exec Adobe_AGM_Core/capture_currentpagedevice get exec Adobe_CoolType_Core/page_setup get exec Adobe_AGM_Image/page_setup get exec %%EndPageSetup Adobe_AGM_Core/AGMCORE_save save ddf 1 -1 scale 0 -60.5293 translate [1 0 0 1 0 0 ] concat % page clip gsave newpath gsave % PSGState 0 0 mo 0 60.5293 li 60.75 60.5293 li 60.75 0 li cp clp [1 0 0 1 0 0 ] concat 59 54.75 mo 1.75 54.75 li 1.75 1.75 li 59 1.75 li 59 54.75 li cp false sop /0 [/DeviceCMYK] /CSA add_res 0 0 0 0 cmyk f 3.5 lw 0 lc 0 lj 4 ml [] 0 dsh true sadj 59 54.75 mo 1.75 54.75 li 1.75 1.75 li 59 1.75 li 59 54.75 li cp 0 0 0 1 cmyk @ 29.769 11.6064 mo 30.2188 14.2261 28.4604 16.7144 25.8403 17.1641 cv 23.2212 17.6138 20.7329 15.855 20.2832 13.2354 cv 19.833 10.6162 21.5923 8.12793 24.2114 7.67822 cv 26.8315 7.22803 29.3193 8.9873 29.769 11.6064 cv cp 0 0 0 0 cmyk f 2 lw 29.769 11.6064 mo 30.2188 14.2261 28.4604 16.7144 25.8403 17.1641 cv 23.2212 17.6138 20.7329 15.855 20.2832 13.2354 cv 19.833 10.6162 21.5923 8.12793 24.2114 7.67822 cv 26.8315 7.22803 29.3193 8.9873 29.769 11.6064 cv cp 0 0 0 1 cmyk @ 26.939 17.5879 mo 29.3081 31.3857 li @ 22.4536 20.7676 mo 32.0635 19.1177 li @ 34.2891 20.2578 mo 38.9863 24.9434 li @ 19.75 22.7598 mo 16.7427 28.0225 li @ 26.9077 32.1787 mo 31.877 31.3252 li @ 24.5234 33.5605 mo 19.2422 41.9502 li @ 34.4248 31.8691 mo 43.2354 37.9629 li @ 45.1982 39.6777 mo 48.2832 46.5215 li @ 50.0215 47.3174 mo 54.3945 44.3477 li @ 20.3755 43.2568 mo 31.2383 41.5176 li @ 32.4443 42.2617 mo 31.6348 47.1504 li @ 41.1445 25.1655 mo 48.1123 22.2642 li @ 8.81104 32.8555 mo 15.3091 29.1543 li @ 1 lw 34.9326 18.8364 mo 35.0684 19.6309 34.5352 20.3848 33.7422 20.521 cv 32.9482 20.6572 32.1934 20.1245 32.0576 19.3301 cv 31.9209 18.5361 32.4541 17.7822 33.249 17.646 cv 34.042 17.5098 34.7959 18.0425 34.9326 18.8364 cv cp @ 22.2847 21.0083 mo 22.4209 21.8027 21.8872 22.5566 21.0942 22.6929 cv 20.3003 22.8291 19.5459 22.2964 19.4097 21.502 cv 19.2734 20.708 19.8066 19.9541 20.6006 19.8179 cv 21.3936 19.6816 22.1479 20.2144 22.2847 21.0083 cv cp @ 17.6128 28.5693 mo 17.749 29.3633 17.2158 30.1172 16.4229 30.2539 cv 15.6284 30.3906 14.874 29.8574 14.7378 29.063 cv 14.6016 28.269 15.1348 27.5151 15.9287 27.3789 cv 16.7222 27.2427 17.4766 27.7754 17.6128 28.5693 cv cp @ 9.20996 33.2646 mo 9.34619 34.0586 8.81299 34.8125 8.02002 34.9492 cv 7.22559 35.0859 6.47119 34.5527 6.33496 33.7578 cv 6.19873 32.9648 6.73193 32.2109 7.52588 32.0742 cv 8.31934 31.9375 9.07373 32.4707 9.20996 33.2646 cv cp @ 50.7305 22.0615 mo 50.8662 22.8555 50.333 23.6094 49.54 23.7456 cv 48.7461 23.8823 47.9912 23.3491 47.8555 22.5552 cv 47.7188 21.7612 48.252 21.0073 49.0459 20.8711 cv 49.8398 20.7349 50.5938 21.2676 50.7305 22.0615 cv cp @ 41.1367 25.4663 mo 41.2734 26.2593 40.7393 27.0142 39.9463 27.1499 cv 39.1523 27.2866 38.3984 26.7534 38.2617 25.9604 cv 38.125 25.1655 38.6592 24.4116 39.4531 24.2754 cv 40.2461 24.1392 41 24.6719 41.1367 25.4663 cv cp @ 27.147 32.0957 mo 27.2832 32.8887 26.7505 33.6436 25.9565 33.7793 cv 25.1621 33.916 24.4082 33.3828 24.272 32.5898 cv 24.1357 31.7959 24.6685 31.041 25.4629 30.9043 cv 26.2568 30.7686 27.0107 31.3018 27.147 32.0957 cv cp @ 34.5391 30.8262 mo 34.6748 31.6201 34.1416 32.374 33.3477 32.5107 cv 32.5537 32.6465 31.7988 32.1133 31.6631 31.3203 cv 31.5264 30.5264 32.0596 29.7715 32.8535 29.6357 cv 33.6484 29.5 34.4023 30.0332 34.5391 30.8262 cv cp @ 20.3408 43.1777 mo 20.4771 43.9727 19.9443 44.7256 19.1504 44.8623 cv 18.3564 44.998 17.6025 44.4658 17.4663 43.6719 cv 17.3301 42.8789 17.8633 42.124 18.6567 41.9883 cv 19.4507 41.8516 20.2046 42.3848 20.3408 43.1777 cv cp @ 33.6064 40.667 mo 33.7393 41.4395 33.2217 42.1709 32.4512 42.3037 cv 31.6797 42.4355 30.9473 41.9189 30.8154 41.1465 cv 30.6826 40.376 31.2012 39.6436 31.9717 39.5117 cv 32.7422 39.3789 33.4746 39.8965 33.6064 40.667 cv cp @ 45.4697 38.502 mo 45.5986 39.251 45.0957 39.9609 44.3477 40.0898 cv 43.5986 40.2188 42.8877 39.7158 42.7598 38.9678 cv 42.6309 38.2188 43.1328 37.5078 43.8818 37.3799 cv 44.6309 37.251 45.3408 37.7529 45.4697 38.502 cv cp @ 50.1846 47.4102 mo 50.3213 48.2041 49.7891 48.958 48.9941 49.0938 cv 48.2002 49.2305 47.4473 48.6982 47.3105 47.9033 cv 47.1748 47.1094 47.707 46.3564 48.501 46.2197 cv 49.2949 46.084 50.0488 46.6162 50.1846 47.4102 cv cp @ 11.5322 55.125 mo 11.5322 57.1445 9.896 58.7793 7.87695 58.7793 cv 5.8584 58.7793 4.22217 57.1445 4.22217 55.125 cv 4.22217 53.1074 5.8584 51.4707 7.87695 51.4707 cv 9.896 51.4707 11.5322 53.1074 11.5322 55.125 cv cp 0 0 0 0 cmyk f 3.5 lw 11.5322 55.125 mo 11.5322 57.1445 9.896 58.7793 7.87695 58.7793 cv 5.8584 58.7793 4.22217 57.1445 4.22217 55.125 cv 4.22217 53.1074 5.8584 51.4707 7.87695 51.4707 cv 9.896 51.4707 11.5322 53.1074 11.5322 55.125 cv cp 0 0 0 1 cmyk @ 26.2422 55.125 mo 26.2422 57.1445 24.606 58.7793 22.5869 58.7793 cv 20.5684 58.7793 18.9321 57.1445 18.9321 55.125 cv 18.9321 53.1074 20.5684 51.4707 22.5869 51.4707 cv 24.606 51.4707 26.2422 53.1074 26.2422 55.125 cv cp 0 0 0 0 cmyk f 26.2422 55.125 mo 26.2422 57.1445 24.606 58.7793 22.5869 58.7793 cv 20.5684 58.7793 18.9321 57.1445 18.9321 55.125 cv 18.9321 53.1074 20.5684 51.4707 22.5869 51.4707 cv 24.606 51.4707 26.2422 53.1074 26.2422 55.125 cv cp 0 0 0 1 cmyk @ 41.3203 55.125 mo 41.3203 57.1445 39.6846 58.7793 37.666 58.7793 cv 35.6465 58.7793 34.0107 57.1445 34.0107 55.125 cv 34.0107 53.1074 35.6465 51.4707 37.666 51.4707 cv 39.6846 51.4707 41.3203 53.1074 41.3203 55.125 cv cp 0 0 0 0 cmyk f 41.3203 55.125 mo 41.3203 57.1445 39.6846 58.7793 37.666 58.7793 cv 35.6465 58.7793 34.0107 57.1445 34.0107 55.125 cv 34.0107 53.1074 35.6465 51.4707 37.666 51.4707 cv 39.6846 51.4707 41.3203 53.1074 41.3203 55.125 cv cp 0 0 0 1 cmyk @ 55.8506 55.125 mo 55.8506 57.1445 54.2148 58.7793 52.1963 58.7793 cv 50.1768 58.7793 48.541 57.1445 48.541 55.125 cv 48.541 53.1074 50.1768 51.4707 52.1963 51.4707 cv 54.2148 51.4707 55.8506 53.1074 55.8506 55.125 cv cp 0 0 0 0 cmyk f 55.8506 55.125 mo 55.8506 57.1445 54.2148 58.7793 52.1963 58.7793 cv 50.1768 58.7793 48.541 57.1445 48.541 55.125 cv 48.541 53.1074 50.1768 51.4707 52.1963 51.4707 cv 54.2148 51.4707 55.8506 53.1074 55.8506 55.125 cv cp 0 0 0 1 cmyk @ %ADOBeginClientInjection: EndPageContent "AI11EPS" userdict /annotatepage 2 copy known {get exec}{pop pop} ifelse %ADOEndClientInjection: EndPageContent "AI11EPS" % page clip grestore grestore % PSGState Adobe_AGM_Core/AGMCORE_save get restore %%PageTrailer [/EMC AI11_PDFMark5 [/NamespacePop AI11_PDFMark5 [ [/CSA [/0 ]] ] del_res Adobe_AGM_Image/page_trailer get exec Adobe_CoolType_Core/page_trailer get exec Adobe_AGM_Core/page_trailer get exec currentdict Adobe_AGM_Utils eq {end} if %%Trailer Adobe_AGM_Image/doc_trailer get exec Adobe_CoolType_Core/doc_trailer get exec Adobe_AGM_Core/doc_trailer get exec %%EOF %AI9_PrintingDataEnd userdict /AI9_read_buffer 256 string put userdict begin /ai9_skip_data { mark { currentfile AI9_read_buffer { readline } stopped { } { not { exit } if (%AI9_PrivateDataEnd) eq { exit } if } ifelse } loop cleartomark } def end userdict /ai9_skip_data get exec %AI9_PrivateDataBegin %!PS-Adobe-3.0 EPSF-3.0 %%Creator: Adobe Illustrator(R) 12.0 %%AI8_CreatorVersion: 12.0.0 %%For: (Richard Goehl) () %%Title: (flipsticksIcon.eps) %%CreationDate: 10/8/07 7:08 PM %AI9_DataStream %Gb"-6H]o_cE@:MfniP>c@j^W7as$o5TWP>!4geH',oT:deXB0$CX2R6^$P%Hp@Lo!qt0&9J)D'?WiTrMDsgYs1R4A!!<.MdiU1Y- %^A@-kj,Y:rHhZLnf,t%UcS)^@/mNK7cD:o`L!s+1rTAC>If%#bkE9UdL-6f^)DAQb[Jf40/T*e%qr>TK&"e@El/T6kJ,&!bj84n0 %Ie``Xp%p]*rpfX2e_c,=hS4F;pAX*MLGthYo&YJ/Fo1S!O71qTpNJhcr9isMT3m7ohbifjbKYP._jfPurV6VHQK#[XnEp)p^O,i. %lLaIfJ,;Jqs*u3>Du&u`Da/^uF:%5)#i5FL^]!0S`udKpI,GL5:g\_F:VVAUqfb8Prq,@E]8n'5q.Em?79h;qo_PW'H2mDYDr+jV %^4#mVMf7Id+bG/jM^JJE`H%BG_V.ucRnmt[IZPYJFoCQ+5CRp*EV&?PqXZJLjIcJ,g\:/$ri9%$]*c<%qd!o-j3[etc2[S/bn#=! %R;h@3hELl%rL;,OH-G%V0kNM.>lC`]BfNWRRdgLFK\b,7PcjCr9is!P,Qh"rr!dpg/?RtJ#1"H]_V3s\"?555JI.0,PL_e]]`-:3+'Gro@m%rj8e]DIsV"N %+$T[2N,-_*>7C$GCa?('m2,1G]>\'Wq[tR5-#H%2"8hC9?R2OgL$u&B5^2G9THkG.?]h;crMOkSJ+qpb>?EQXRWp;Kpc"i%I91!A %]$Kr&Bn43coD^\H>>q=X_.Xef$VW"gPk`J@\PU/m6IpX\NAUX$TKcG!d4iuPQ$*OYACIo5Oo#.8>dd.6<'^YIV;cM]nT %]t]U?Xmb`Kqu+48&W?m#_+,WjY@qXb4*UN.Io-S1i@eqs-DOOh"%E!C3hQb.6+:i#g4C[L`A^VH^Z\8a,X$c(quto$7k&*Q9];ka %7kN)A^Sl4'O"qXr?nOPW/dds7='>smK"^(p\-[7bDI[37j<&,f55[iF14hj#r$EYCGLQ\B2T!,?UQenRPr%A!PF.&F?bqeFu)sQ=Qs,$[)fe.,:?eK7t1X#PmARD7fjM&bY %>#uTIR9iD:\6R&T(`=^K\oP]-21,k9oIDNFFl@gp(Tk44^MT0AlFZuc?G>$8q02Zb"=^>=nh2Ji(C+*jd8cd[4)SQik/ %jo9)Ji(*[ZnGK]os*qHLrU&jM]`.q.c0hUClj %D-F/=?_Ja)OsIg"hl#48RTb/L`2qfqIS(-I]@pIN^KXqm-GB&GFM#Am=8^aTkQe=6PAp0'A[D_If]9NGJJ7demLJ?RX:oiBIQPn?^4U=glg!_S;%+EuT^Sq#!+f\LG`dbbS$C_o)*u,eP<.6%97\H*l;g%F3>NB9KBsHV2Boie;9]sQ1$hA\',0?"/pB7B$%@le&aOYIg;K[]U>qpKZ$^am%o3O %jM$c+de`LeMY=kDPd%9'E^DVkoOm5SkYR7D0sQ&Kg\AFo5Z1gX!!DlCIXdk0f%T3'prW;,psIEOdoS(HU6cc>Ta/JX*02QmXhA=% %+u;fi\5c;Uq3.h3<-rI+,9lr<';/X?*.YDPoKOfOS*C`Z.EekM-\+_+Q"R<*MKnBNW^nKh)#cg'4l/h2Ua'iG,4Ej4."Q %7V`Mj%m'58*Gk``%r<8-ldHc?ob?WteJYqf5bDP7TK;gCY!T/QB(d.d7CJU/^B<&12j:Jg&'RkM`>l]b@+?kBci5[GkuMnqe?E)c %h4CU6mNM6M@a@7A^+;75u>;fd[: %h2>;J)FXVuFB,?NkNhsb4c-8B:?IA8Dea@7Hp2>+/LKG,>'*\Dh/4a]NS4K8X`b,"Z'hej>%F0!FS#8Y1qdL+mmgSBR&a4u2Fp&" %/"_/m^)9/9Fkp4aj6U(?FKC)8^0"]RmteYLh=9rGjmib"a1U)BI[d3Ja1e1D_XmH';C3ojN>6ML&W:%4,Y,'P]k.o5b>l/<*W'Z\ %_`78>gM/rPH_@Vs5T8CuFnINiW>XAY40r6G_37ErgbGo,W=KJ+F`]B(Y9sq>'rOtfe`gcVnC:6mc %fu[_SE7V)1B0+A%RqCM(Tg@aFf"tF0]ra6e>EuVhR@29$>Z[G,r(g\%NMjp\eIJO>AAfA*>sDQ4g,lI+lW(?W7*hs*od1Sa/J$gD %lD1160T'=,11X0Algh1BtBpo/cZdl,Y7FjT.(9ZsAr62a_`ko29?B+O0QWYW%5Yp9qltgRR,.?&Dc\SMM %i`>/m\_nD)Oj+Y!Xa&c>.'"K\;-I-V-j*]g/>)FW)&u*g:'ng,Kk5cZNe#2.`Yb051;`>BR[F1fCt' %#CEW,s4)!ASs?a]R1j+22:GiLd>fFo*a7)3@a,5*`J]uN@6WLK:WBb^SZQKcbP[t/18=NDf5LYIaJ@'doDM*ZpUurCW9`V[6U%rE %/3Mh.L?[:p*PH-L)Rc`Q34CHhp3*i^M0=l@s5Q/GKZC2b:(YOA(R-G@SMgC"UKkQ$,82EU:Q2\?q9r>7=$^LY[&tI/fT0K]#fg>N %.).,cd714[AC]b37_8#qL@ALnU:PM&LVaOHlUTiAK]>13=8tm*99$)^]N3R]JE^$;2oUZdCl2_f]8E&K?2P %_Kk/hX];"2S#J48:,j\+?:XhJ1qTk[2*+h$B'=YOo99$Lh3r,:EgXLgfmP*/4BIjMf)/F)ImB#QSPZF\_ %$\-H?)2i6VXnUh1ar8TnhEA7rD2h:(=_hMPSH^?T4aWZVc`]a+Y/+'10M-gt=U6_:OF)%88%E:-IJua1Fc[-$4U$d=pRIREQ(:_5"p?\d_:fa;>B9tn6\'[j7I08kB %33/E2l1=(9T%IXYX7pMGP?QM<8]Z'qSRr/p*.-0O\0$,"nRj-l-'lT]dZtK*[UW/$X\l)/;9?YPXniqmA`$"]N]YmAgg][EQn\![ %-K7&UhJ[ZTYX_M=":*I-::B0:3l\r2Vri[.Cps>dYJ!,Lr8$:s3LiOh\rh)IGo*?e(Y3BrmAq5`/c)U#`*Pr6AuKFXJtjhSrA/MS %jBQ/>\[N!..j4N!$fkLiL#//08`F\iCYci()[j3_Sg@U>Bp9HFIJ!'E=`U[[h$k*4`?rUpfI#Vrb,rJPicPDg7J*kAE"/DgkHn`n+(UmfU0aqNa!B %e;e!&a-O&Tp1.f,>,r..GZe5iugpYS^?_5N2IP'MAdbJTn2UZ1Y5ij?iJle#:p#2q;*o+4\>@pa%Z#Grm?p!Djr(A'^gXbq?9L?Yi %TF4ClmjafEH!K(?FY>i,++?nUbKdZPf.rOWh+tPT'nIr?003)Xo:`TqT%rR?04G_YPjVN5K!2n5?)MiEL[d=n@]O\9IAu6+Kt.W# %?EACF[I=+Nm#P3=pR)q30@VkoIm:r_``a.'G]#CKb$lmH[(lOB1Sg6&pAZn_'1QuP5AHL:D:$4=kKt35X%0i3k"kLV(==+,@aXRc %WI#$d+0o1Z3i?;Q^LfP.U8O&uD0X\.fZ@HF+'WgDfAl/efK8;GC)qWs^$bd(4C>L3dF7Pn,9*6;ZnP_7r3#4g4<\QiGq0Wmk=UU` %dH??P[lbu$rVpA^R:`*TZVTK(Rh7PBM=l?>"[1\'Ct1hQ[h-)/s$cW6fB_DmPuuBY/S,DS/lPcZ4YGDU^lV"^D1#5K+FI"%)Y %m-JKXCS,&3R*7;QWVT0+=n:3lBfGkd.60>CkKMW89<R8+SoZ2[aYjNb*9W2jC?W5k %og17%J!=)o1Kj%E6T1@^P2F\.ihYs[]FWf*^TXN3-)S'O>X&51TZC*DeQKtGZGg,trXS/QgGALRU#k6%6QmT2]6*]%eaIKI(MrX94XSp.NH_tG,D0mW$=A/\o %Q0*Euj`@Mp3kEAhNifGPkA;Bqd4BYS!e&5SQhWFo@"@>DOpIWK6J%o.O`AG$M)?ep/*%`ba6e'5o2Vh1c]F"-DS1gp6= %)gqGX&&RQU,crae]))'p-O5??57W6nr$hAhoB-#KGJF1Mk"gJOp[&C)jS&^IIX;+"qtG"m]rg\tX1b,KQ@FAYh8f9YIX%g>fTc=! %c)8nV^4$%TRm6rBq$p#ZI3I^/Le\?p&4Pj5G8bN`V##KkJ-ibk.>i5D1@$)=01ZCIWtH?5(sq@b(Fkm-DCQDYJM1j.>tWn,(Xa9uqnb]D__D\QZ2F..sL*Y_W`OVEGX`Oa"_k6jHfRiJ%a3E %^##pJ[D*4'O^@95?glTJHu/e:=F,1>chOVq]-EJ(?G&iWVLKtR>VhFYM/C0r@cbakFi7st4CX))?98\B208JW7ZKs"`Y'N-nHf"* %%<^U&:FlhaM(9>Q%*:(o%S75T&nD?T"W;ro609;:O?pr3A9'o>GR@5XF@C0:oVMSDh$^pIMm%SVrTU[%mr?CaZ7Tta93C2:QFuS- %'liMnjQ)>oisLHVFfSle)V;@YF^M?'CECr/+pf]TlaCYJo39l,$%C.-Dcb[N$,A.7o"CgTh??ph:Eh!RT$%1.fK6cpS8R;X@]@VNU>kpR0o %LN+]@LYP8f+o@@M%sH;&^_B%1mq/uV/0&cZ:^@5f@-nXiH]jrB/@-d;dn;&(98+u:\%GgJnM65OJag9&0L=R1i>i2Y"18*P\rd-3==M\\9&PGZ)7ade0R^=KG %NVQAPJrn_t=MmJG6H_^AMPc*1'l>Jt<]$uD>g^-[G?+7+MXnG4b-tBcUjuJF[Mi7iY\E#j#G3^5%nbL(E)FKRK5Uu84Oq6F:'$j\ %($0ceRTK0b.loVi^(jp(HM0Rp^aaQ3Y<./h4_(>nCFmM`cgCX^(/Fdbr!N?'8aaN`IVgV@Gk$#dc816o7(QgK\7Y!8@P0u:,iTO6 %'j+W^0CGW[!3HeP(7fe,gN[[T=GS(h&>YkN$0%1o1]n,N(JN0V#5eTq&bD-=lrHe+(,ZBPc2s23ja5O(um9B<'NFM:uK533)qXR^:D4VEIY'RKdnLXbmm*4aL"+.`t`?W@1_CW>dL\$W[G^X1,aO?E88*H0J&) %@n"T;'2.W1b')ki]kH[c`D^3X5V-3J`iB@3f2uj"D\h,i,soOM`WgPDT-F)_rEdbl&0nrX/4H>OV\s.-e@JS %o&\u%f/C:m_eZ6T;u>[]="bNmFBU:q-oX8Cj^R+#e$;C>r6=K,h`gc*oafOUm_+ilgjZf\I(dU#jRR2jT;j(uk`7o4`9i_"8_`n4 %)mte<=2W(VbII=Pd@L8oG(e!Rm&,)"e&5V'*o/40BHQ@!ooert^KBNmO(iICGcV8HTU/efCr?gH,l1bL*e0n"f=/^V2]$i/Fas#Y %mG%@O2h-;&T`T8GLB?aYmsOPQ2kU**Hs4a=@WPhfiHmK12`AriQtNkIYLsE#'+FQUH=:>"sG[K.11Pa:Dg8"YtoQHYN$q\+2Te99:7(a>h^NZQHa.ib?cH40j3HH8i:cm)R97A`3..&bb*d?q!dG)m<9A]f:n2Z %OUX,tV2i5,6L7K3pH&:ph94F%Baa#;op*ZmN;-[cm$t!Z/9-(-MshpK^_*m6`%#okJAT<$[F_WL@%TNcNm6pe>d:065jp<%nFs#* %7I,!$iP\g[f!oX12[:hB#s>.1mK:8%NctptW(&te5Ft8opPE@.c4o1?l\$"2UCtaCriJu13-KA+\+4QpQg\7SHc>-4jE8n\;V(&p %]roND>$lD2k#Pi5&q?(2TmQSlq:^[p,^`bNAqqcc`9iAS#<%T<*:1UBWFHu'+NWA8(:>8kQ("s;V\He]7*(:3'9n[8S@B](d>I+'W:N]>_0eC?D:cU=D2d&n@-rqR1/guf;SDBIe^dEr7VZ_5p?(UWP&P0<;]GL@ %3ReXZ%F\prIfbr!^S.@rkZE=kTnqiI)5OTFP+Do?Y-Yo8]5PshgFmZ@2 %H.BIg)#VN7Y\-R/roUZI+#0G7XT&n@qS3$gh<29IV`\[Y9]tgonR_UlD>=,o2kZbsL-L#hlH?V.>'[dU2&3lO5=hc=+rTM"L%X>W %FY9ak$0-)Yr2?j6j9QA_&6Y$:b2GGqDERLaio>e%]TZ<=?_P\,@QO'L$duKI)nGQIrUr`U_ii6MJ,D*Pr@dg5^Jj++?MKFc?Mr8: %0,NMcq*4is,^17me.V0@ibL%/iNu$Wq[t>Bpi?Q1R$[]0f*'6B%iskWaTDsB;>Fp %aR#GA@[!T8'qpncY&C,7JSDVNP>3j#9o.K@Z%6]K*d*[31mmnH%?Edg4i\2<^-+*o1Y$SH-56(H]ArO5N[ZP41C9KURMN<@9QX#[ %.je=L@lc1rRD6(.C:9LD&YsO;V58lBV5;.B3\;^XB!sBU5"I7Xa%>./bIMFtm`&R>3\<0c9Li)<;1=M;SBI36(R(3WXIg8pM7s!C %AB/B/f]>_D>A-5P9?Q+r"ggI*mE?9`7%Z$:$EZF?IhFPr%/$0nYBAr/7#F5rmBDFEEE!G^*V,TRZ;".TNuu8u"=Df-Ou%-c!d[UEP^td15nlq-n">;^\X>Q6kLY`1PJ!4JM+tTE[LUTK91:'= %=@Br6"@e@b?EWkXO-<#8A]r#J>TR)QB8`VEiFiRC(<&'.2#&XGT'bWK'n1Nbh-g<4Oqi_/EE$ia'cX+gP?A[kH23/\B$dVG`@ %_8tp;n`HfV_*qB'NktXPL\Go,%#1F.#2kSuTqXis/DuTNE5"/Jc>c\kngULO* %NW?.WS.S=:Us(&Kih!=)`"7Z0+.=tN=F!,]lV"AYf98Bqf#AAHhF2sfH59>?%kK6$6aF4MgLT`4>sCI"op;C'd^%=I=tkMOXj=2XbJgAbYTadi[$'PYrc1`VIds"p#)PTcR7c`,/I*^N?'7#:]9;dh/&YbJ9:#p.Q1'T1L)pe]#f=Q?m;PdfbeNGFAXc4T7,'5nQ6koj6;QJ<%m>LhWYK3E:9`g9)T,;^Ek!l-_/O3XNFZ!#`1KE2^BT %Y1_0([`_3H-&chC5\ne%DGimob:rOS4,k9n*%nMr@RO$EijK@oEQ3]SR?I/HR[oQFhXK5gn3`q4Im51$kY6V_T8\nT+ncHUEGc*9 %Kt>paY;.JO'%N@&FthMUa4!Yfm-,]-rOk'^gV'NcEr0p-]P.b$`YfM6OeHL$$:IEgB8&B;0n5E-O^:7bVhUd=IaVUb/hZXN_@1C^$LaHZBE`ik5U% %Om6qBK;tb*Ag;5"U;+cKD:o74[Y9BTT`o_l %RUe_RLp1]6]>1Vjk,^r0K7\%?a=RCeRZ!IPZL"J78NVMuo-K\5[Mb5"Sgd7LQCT!#G5.M2*n93LSSEL-f5E:@]@+E"g]]R)'$&AD"Sc$n"1WU>c]#ZU$(7Fia!u6=SP9F!6XjY_ltV %"UlRu`:6soP5#](]L]T/c"lX-SjFi6iuJOK'"2Z/_;$/m)-NU/k&O=q0!X6oY^VsgjC;JkM&gq=@/[pp;M3Q3KscN>O>!kp:Fhb@J$m)4iSM_e@uULN;,BhL.cTe.7qdCF[uVlV$u&XLWRc*L%P8f+`L4/=%$flZBY %Ri4/N:%^NoZtCd'LZEtSKJ[5.KNPCM1NXXo(e?!1`[Rp^,rd_k8RIU?,0i/PKQ\7rXr3rd3RIU'RXm<@P03pH:,ZMKX%MtS:6AbZ %6l797@3ZecnTmri!30Q9WBenSU>t==ZjTU(7,j7(#p=njl76N`:4= %Y*2I02Fs]&KhG^Y$$Do@dd(HVkCpkACACa*/MKRj.PC%mJTZ:r=U'_Fd:i-.FhGUs;M&WY6iK&YU\SU<0?,]o+NkN.C1P(??;3sL %=#/dD8e5Gnqa,=6iIFY^HsmNNDd]f[_T&1IWdmZhJ.ib_fP1q33:Y_7X-eda5[]Dp`?N!B1V@CAP.&'V=!Y>dU'(S<2oAJbeiXMO %1ql'L10^ErR\QRO^I_oc$+?4"E=3,5`9\9UY`tr6?7OMRE %Me9k(4W[DI7C:nZQ-a+[:96Xk/uVmnrU(7P+)DPS$7JZW6%JN*'b9bXXDqH!/38*Qj\fK@GV, %O7q6B85eh::/Jj_8U30kGS#_mc)\bo)gDdJ'K4X0%Eo_?@o#egM1H@WBGOjiRfm4)]9]Onl65'l74tX%81=*!&QlN_?tC>r7NU5A %0(8;X?tq9&]a%XhlsK->W\"@:^U^3M096=\CgM@6YXVY[h6"6s">;8D]Yq1VQkLTGXVe`&T8[ %Ql^81C;k52XM_8>G:oc;X:[A@#o14S)otX&`"OrM)pJI&`TUOZCT#:9@6ctHM4_n)oNZU#jE:EfjJj-lAIArp%, %`ba2EAuTupPu5mg:KYN-L8DM\Yn,,gc`8E-;@>![:!D&GaCV5S^UWe;0;c %3H$biNtan%N5^XORKgA8P/Gu]-'YhFVBb8?C#Jbk>^:P2V\5gLGKYo$:s9Kt[pW8@>0U"Z33abH*No6_@+g.&afpR4]K\8KCKMM, %hBPumZ[b$@08hId/s5oq204=DX@N@#?[^*Qkd^[PXpiQ?ce/r9l/G:;*"@cY#u:r4'P%e,b%suPqp'!"/fP9KUrAUS:Y%<.Q*\6rmVTJ9[b\>QY#.`6mlCK=aCbu5Pf3aX5]Dnc=a5\ %e3C.Hc?-)L=KH#$n_>-NE+/Hr%?n,dd*s*T`ZoG'AeCGhE-m5(\YtI+_D7Pc0iaOESgNYi(Cg#,.hNX_nKpH%7S)GP?qEU3D+eVf %,:^i:)r\Mu,E\bh!\A\(7nAr6]1YeMOoI7,q_S\(KP4LkIiq03sM*5"rkK\ %Nc>o%@$gQO5gT6U@M=e7b/f9Q^?MBH5T\4cl6bKjLZUQC=bU'sAa%Qb26S)`.%"4L3Y:'3jq?tlKR0q\"Zr(_*!Gt[lV'p'lr9Y4 %_I?UOCgF;(2c[qJGN@!i>+*O)eO\?g\q1?FKEXUV[Chi^.50u=0?`4HSlqeYd*Z_eFbiaG9MVZ"DJ^T;YX,OY'_dCc0UXlT0^u?% %0t'75*$hTn'@FoS.dF=cBftA76&62W2DKl7n2go2ibokeX%YNlHZFn'7iZhY@K#N!@0`8qr]4h.ksSXAP]ni/1*UTTqsE&fj;_`h %+*WRE]$Z(cYjH83PR,VWAP3\`ChAIh6"#dX,_iA^J3FNLQGp5?Qh;(pNL@aKOC;B:0^MS)Sa-eaQ(9"W"fj#'bp\WDH)UC]e.g>N'D(Sa9Hu.c2E_*^TRQLDphd=C-u6=)O,0HHMj$>cB%5ib"P+7KYp80>=VkMLk]DR(Mu=P.no@eK_LVlD;Um$ejEIlu %fn^(UI7G$/#pH6E6L*J>mh\C1O_fFd8ZO\3n<72fIfp78)Qs`=:nB7R).;oa"pf[AHIQILi3E>N(.bKq0kBf[uD(W?0sl %@p$0b9=?lf=K=5jc#/jCYc@5K5h+eO'')S>,0j=m-h%+?>^ieUs%(/?``LlJ/N!:Ul/H4[pk5'%$l2bgT3uQ!*E$3e\1Ag/jaq-6 %1+A#.m1F#4Fi(VMDsY7-cBZA,o+`B/[RQFFb>U3q6Yl!F2Vt\K>IBHaKdJrnUu?,?.gJ'qK]"#$rWh\HN'QeW)1AW39=9*1"1b@(mJMAFplfU\o$g$9[H9)X?)@Ye9,\:f)9`-Bh*pINY!]kMZFlaoZ@*&[EH/,r@3DSXs6s+'6HCBCUQE.nDW_i%:R+2VSPg+)@e1+D<9+mDdA(,;9glPE8o[T'TPhr#W %&F@#J&-?>'SF(fP&[4-$#-2UJ@4MpT8A5mI]VWS?3ZdOAigk,N$/5]CB(qB!V'USuliR;5^)JEF^=Pb6[RAg;G9r6hbNR,a]G8/# %Y_T;C/4DEDU8R4odK)Q;iMK@0TTb:-WbcL2M-34kfBlJ"N/]bNr#cit@8!:1%#8+UPCOhnqrL`L>#d&F-:;eN<%,2g;8FJ7B.JhU %6!,eC7'KW9*.hne+fIqnXq%mcU_5uYfbTPk&$G'7WB=Q`a'K[M5n(D:,n+0B5fD>#O(^YDff@Ta %cmXJA][`NK;XBndML\CF#c2[*k%2[m+4O`p,F"9UC#MW%E)33uf'51;_LWCW;Q2PpOQV4D%]mK)^\A*NjFpK7aTFtJ"87&`!r`(K %L._<1RD@H=@`PRn2WSiP+l/6],pXIRq1cPE3$oWN@t@%A2W53jkV;+:7B4cZjcEQ@1$9)T:/9TQ8V^EOdY?+DQZ[PGWu/DNE0>F$ %V0SE$_+Ym$ojMR'lfnXe^bF!_aKmq61>Tr-_"N307P75AS\I&`[FA,5CXn-0)?AiU^_mgS0Vb %_eTnjY7'=p%8lgrI!CBB*((QmRDeXKSUggs#P-""h8:)O5EL'fRXgBB,`&h4I%V)'+d`cXff14MNTTi04/+oE3$8&s:UZ8G'Q`PWXdO\gO+S[jSM3n3!d0TMH0m/:75Nd=;ZTLO %#JRMZi6%)"_J7qJCG!>-^_TU^/hD#mVm.%KlMB@F=?.b`jj%>NaE*HS2&QU5c4pPEXAk^!$+^S^pGWWf#s&YqF17KX$s>\(3ES.D %:BF*M%hcsjjko[[?kV19=m)#>#Qfk"Qn^LMECu43:-?`+of@AR5u_A$4^a9/O=+Jt2Zk+'R;lYA@Y)uAFLF:SEk(_o+iIHI'eS3$]o8.Hb"0p2GUsJO'>rgZnomJ-:lXZ_Z]b@2ObB3MUZ'_N((+ %"X5/3K?-*l&G)6P`4^P1ASm3nYS&Nb@]-#h99QfT$pd*L72m0oSB?S2]<_XCTW(@N-;7$(j:JSLPuU;Jk/X.8Qn<*&Z03T1^Si)+ %;=h*GPn(NYl0134*nYpoAW2PM8N2rF!Qa@+$l`)Tc9\`L1>=D4hhEt.qM-DSRG`UfcIh=u4r;q!_jao@D> %0b1Y0f>93Y]+WRn$hHX-MbO<`XL$5j(4pB?@+We/1tqX2[\^55b'j@(b">(XXN;dlfPD@/ep'R8/Qc5jCmtU=aj.?.!ead+2F+iR %J?@X:-O^+m1VogMHmV-TU6U]>jGm;aYPSI`qF94R/)(1tZaOp`bq;:l:t/2@albR`A<1,4gnD;Jcid&-%KXhH@^t\-!A\/NC<^i' %]i5^hkq2^;'-PGi;opn.7=[0.\jVr)_!r5qD(f'88Rbi#"H^.&%Q^7Wgf5Z9B-mj(JE%UbUbrm;3(h;r*6[T):!l"'rSk6a3Z\E4pUAm8:t&6V]> %/I8u+gGT@`$G=8KZ]Tnd)p*+S$&O\NrKjf8#(pIW/[Zq/I?O)N9+\4e2\Wu_/QlW88R;Irn"m-?JgG`._f(fMBk?4:[1'.!TX3a. %?R6>B@FXBKB1-EJ@;j6t2.D%;`Z^Ua:1Fnr++s%dNq_8$5U8d,](#e[9CH%qWDC6c/CG8rhQW*k]&%RV-H.=2&72RS>VsC)j/f\% %<;)KkckCE%(_FWqP1>u)j(lT:P33&`5GaN%`Y/cA]%]q#GI@:BSAO?m,`0(RfA..W)'<&gpYorMLMC`fK8F76^U`ho]Rr"M%]9bXG %KVXT,34E@`.])DaN-(RQpk\:P6fi,!98Fr"+N=SgaL2dGkKCi@m^$?=iA_9A'kGhtYXB't$UHOsh[9QnIc+pD5RBV3Q>XW0Ce[11 %`:LF.Xctg3&N7pr$'kq=Bat7H+U-&;+)*]YnG,lbc,dEFZsQoT`-^iG3gG(+FVQ?+%bmW2e-[nZ+M:sA#V.e[Yd$1EZP(?LiWeCVk7sH`@lB'@6Gb/N*fV7'>D<(=UVMp*e@&LE_;W %#X2gajK>+gM6:lgO0U#943J$hekHcUf]'2Q?VG#RjrrQ\*_i$_"#kF)]B*X*QA@V21#Bo;F_s8EP\EUf`AB0Zt83pF`hkV,`M"`sEnJN^K)l!cY %e*,7U$O44Z?qE4312*$qL#fj+K@:KU[[FYZl&2*24s%'e5(SBN(.l!li4rfSH,3\Kb&$2lBV`q_hC@q7+;B73O+k;CJ9F %+:fh6.pM]l(QK4H.pLSV+OBI(%)[0h@SHtFaf0)YEJ#QEV@Ml7)S9;0-R-D`"m-'Ir'?5NM7f^R,['JQOs)nKWBm70JPROaY*KP* %XWG.s)(iI0:/OEF5YW&,RhO.VF')7UQb-[&ZQr'4>Y5u8bVdLtob5YZfX2L)?27;NP-_-0pEdRa+REq#!IJemP!cGr(gnoR1ED;K %R"f_\CiHon,uq/Q`Q^#!@Fej"o*%Ld+KefAJ]QBp*6/++=]m_di@=.=&LZJ'gVkT\"K#h9)94Y83iSL-VFMba+gS)/aS]%QH8K0@gD%\B\nB#)==o`H9ibSuiPE#Bq %'^)camoM^B"Wl6aWF0)./D-4L4EQ0\Xi1N8p/LLs_3+f1@n3%U=(Wr*CLX%p>I"b&K$f*1@%>@U+0Gm[j"V<"7%5nD\_M\^POVNo %8BednX,SeVM%!du`dA2>LQI(c.2?(lZPch;Gr*HKUG/O=j\I$d&Jr:Y"3)N2Tq_"2$M%Z %BXTD?Od^=p+k^.gnG,"#/X%6i!X*.@f_PFU+5pGID2jG/>#dAg-"eN4-#\JF/3bQT;5Z(U8e?F1,C`?;Q]+LD*f`d"Q\;l&f7dFR %HnLIOWY\;$[(On5J8%De/C(H86@.!6[N,o>?>ST@R95gd3%S1eN[9VFJ1I#0PljYl_Z?Zp9BHXX*!9#4c#HXWZsocj./.O\9su![ %P@Z`sMOd>?aVfiA/`'HjXTk:=[W@!E6"ep\0LE%Wno[Pb(J_bV^]g33WoV!,5cEM*m.7=BjdJX?L;@95na,7t/Qo:V9la"Gk[8H4 %$2<;"76qn_2RaL2Jdnq"H@G9#]Ur'MYeb3o5U3@BHD-PjpPkC],W#hH*=/=>k. %nThlQ`Z'+A@5R-s8FR6m=c(fT0p]/PR_'5XGF%]icZRPG2)Qqr-*sF;EXF&`!bCo;H/f\KeY[YT>;@tE\[TtlB3^aHK-==N^pRrm %EnE:8b>mV`N1K!_28@:L2B/9+9[uSpSIX.#g[%+-O6'UY1C*fG!$1cZ"[tr,fn7Ud*hUeLUg#2IDd2dpqIK?f+leOqIe`'TX^j>o %3^u<8C?PpXKt9Y6D9XWjMdB8akkmjNn>KD&6.C]OGA,T]60BIC%ZmYYku)F=^gO(7]R*go,7hS;5mC?K1J/23''UDr0i;gP_U3!S %ab-,Ee3^m*d+9OBQnq8\jZhTJ^p"VGL>+SZf3NEl-c_u@HOWu4QWjkp=,Tsla)8SGRA">$uq%Fh[=rp %>uJh?giP+1Cs4n3R^7u-[ZKTEs&RG%AA,g)X2gBC,"48lE3YGbR,dlahGSL@!R"d:^Z-2EH.]0t<1[ES/$[s&T]s*Z(m3.m %j+(AC(35c.*ac7#huP4=Ga`5hfn:e%Npru2@h2p`b1S/#.4/k2+q1^f<=UR)kZ*""3B %K$G,3Uu6=$+sItR>O>ZUBB^Q!b?55SB<0u2a3Y[,(4AUIGhD;P;k-&dZ'D3c.^k`>:5?pfB$P4,g#Y`48hke.T#-N%m`=sI9:Bos %A%c#>\_\prZp_HWYGZ"krdZMU>Z''S^$K^kZ@F@dOITp4Eisc3Q0Lfk;f7P:H9d>u-ekcR\-.qtc%qA73[[*MW*Z70OJG^8Pf_1Z %lB0R[PDt3u-]iQ$_$>kCf)Q*Ci,h+[Ar?k4WWLVsj;g<^!RO,u/`fq6`4]"J:,N#%:LCHVTE/Mf9A-+72#u28\#cDMP#p[M'<%GH %IjhnA$+B@$:[M;BOdbP,kA:>?NPp089!R6]R$n."NX0(m_gF*F][QZe\#g1/U\@*-m^TjljCDM0X %&UN$6XP)*t\`Z?9nm^`A`['8uogm&D`b.GD*9@r%N<.,RiAQQ*-'O?`0$>H!ja]ZX3l"fccmfP&fWOsgH>=A?[^F''pe2ad4B(e3 %F=u(L,F'*HmE^IBBso"sQ;Gdj`n;Sro+W13`9JB8k%Vc+3C=[R.N6t#J3BMVhY7[:n"h)Q0D/AXN?RfYs,M;ZFuulI<]1g-dp)/O %cHUCWQb%)U2Vh's>J6[JLO2o5F+@*5\%Z^>q5EZX+HFU"IiR@jTWs&,$R\heI<.r %&5d-8nKMn$F0[B!Du)j'oHKV0V/hp!X*kc2#CdND;R9Kl8C71`]j;V8KkIYI9$ZVFTHo,3a9S %juFtuKM6?ff;ql1n&@[UEHGrt_M[p!\q@lENVe$/;<>MVoO>Z=>nZ]$WQ-pOU;^)s]X2TYd7D5!8l %MBF#7N"CuRUm\0),"H=T@d(G2n?&E2S-YsP_i=R2K2=?+c(bl:?#Ga_fEA\T1J=$TCp:Us %N8U8*SJ.A_Qk(*-27,,$D=$!\kCiCQk\,QYse)gZ5:a+29A`YLE0:l_\.jHFq);f0)B4 %2nd$VDWS=UOg;;^!ln#SSTA\O913:k.PAb:1NS!a/kF`;dEK8TL7;)bg;:9!c8t'u#c1Pha3MCO&o!Q3%]Yi]&4ThL?C8X,/W"2* %N*X;r.,C\#>3$SY*/U&B4s8kG5u-D?4D6gHqT-2<58HT=rQ7"8&RJ3H`&K-ie2LEtm-@#pYYnoKUU"S-j]Q+Y2l[cl%mbFYUT+n7 %5!Gq$l%0"\+J1]MR_:TlEV;))HA;uc_QoaJOiKZP*'pU(4hKQpJiMPWauuo5mssTZGe6@Xd7Xj=+-l^VE1ga,MVe7jj?kQ4EK1^q %Wl=c2O$rN(75mo%:UaFsb8`P'P-,`$b3$bcka;l?:-J,-K[n\4GgMWB[6o.b+jVS!XUH)oG0lRc/bYUl8CRiZlqC0U/[Y&WIn9ieVu`@HWnQT/J(S^HM+t);/i+FbWXr%nR_* %11I5pW4g+M;QPre2Hn.^SbOET^?T^i=GLCgq(NNYi&JW)V$7)B-\r!(S\Chr4c;7#CE.Y6TkG)kib/]YdQTu.,rAF4&#lgp4mWD% %I+tEQ-@AV&l`hmJ"L5Y]\!gcI`Nb\F^FO9;_4Poq\S0`Q*nl3FRV5P2"D]*YLQW'<`PZH_4Ue\7M;FWAO1.ZGe5j^ofY$?ho91>@ %Iep!^=MLUe:1_C3^bK)/S\>VaKYn^A %d0K"48nTSoJg7^a1%TT:I:l7a[dWDf)n4InDsE:0FE"UHPqe;q%mRl"*?;mab8U;hO-(KY'?M2@1X[JMmR'pm/h5hI]p][8,UL35YJtF90i4;B=(]VK3,(@Y0A$cRNjZS#q*OrSRfr;ePHlK;3:bN5q[bOTmeEK-Rm.*C'2TO)Xm93Qb:XN+Z)/ %@hEhNHQB/[qS>,N),uBU3c=0!ol.9tPj);7510G/):*n-Xjd%'O)X`"T,*;6F>d]h,BA]%@7'..<>D1>r?'fO+Xk"Bekhp+n.F\1* %9-;mgBC"I]/mEnYpTYdun9>-DRr,^YD(/+%EsO<>qS0#2n,p/\pEEBmT8-Nd;lB*%pMdL!Q*JPu54S\$2fK0AWtU4O_@_ej7kf`C %p3Rh,[ZF"3F4L)cn@RC*M`T]qn1?TjWZ7I2LOM9ter0uh-5)Sm_D4B_!c',][F>On5N)mm*hLIO+nr)O>mh%GSG8;VJXj*TBK57O %eoG7bY(pW6UsL!berLcD\Uh_YP8dV&qsd0i9_>@2k2Z&RAj\DOX%4K`P-`Di#>W@X5,(6VbXr_;2&Zfr]jkD49P[d'-(\-6g(#gG %*i@ptlD6jT,FueSe6<5GUSF;6*iu<$?O;T$?Ze!9QU/F)s"GOa3di)c%nGAFRHiDP7iL;B3kF6YFge!)m0KW??2Wn8f;UBO2D#n' %dhhqL/lu#0IVgrDXYKJ+4".5jP"/YDG>\7iH73!K;Xfbd@I,-WTR.r/SW6ERrV&9j]kl*aN7L@K]EV1Xl78@>R,tP %1duu)D+bsd%qTUN-luFLd)-..Sc`/fTTe_nVbJ%i!_VOAg=S3$r-bskg)I3JA>cHWM2,DH#Kp:bY\K<*W;YZ6cPDgos&LigHto4W %QZRah:[f*#G&-bL/N'*)//mI!9^aODnUD$[esL*j$Xf1DGYJ %CnbZb((;TM[>l8Mn>onEj:F]-;]Oc8%\@-@[GmmTG_@G?VU20"mn@P\@S@dltY' %4*Y&LW'afOnjdp[a.3P@Dqh,Z<9$k)pu6$1CfI2bo:>3N?)k!4DS"*"lJ%_2T"2eGQfUujUNl#+Z] %gH8<'2cZ(OdANHBpVfF/&Tq;g`27#WoY-NMn#VG\q"=L8#@'/2Cb7U1:@QRH!/BngX'IpQ6epTJ>Pkk45ih8_=CSaBgWJ>-R8tiC=a\9<8(S>O; %?en.!=LGQVNq*dRq9#rgi*e%`'DT;g/^M:>I%e.^gYrRr>0Dp(\/q(rX5&gJY*/2)f^P0mC&PC %bo[\DHJ-PYXk*5EU*6AYZ'Q5l[?@h)2j]!CZ7`@"lau)MFUH-bQ[u3_b0:0)-Lq\a*`p;`uD75nZs %L;HZ/aQ-d#Mf-R'%P!0KG*A_"T6C;8@#M!RJ^('#4u %n/W3[U.$p>U4$i@PVrN9GZ]s!LN%d\q^P`Y5>;9e.:[]*\(l$%80a>r@!DF[fDutYp7\V;pQ9b]g.'4AjB27t".U]W=`CW,H0?Ip %iNW5pl4iqq(VPX%.H;Wm;:"CaE!tr!H"pX\riNQda(gi"H0?LIE4AUoj2W%&55YE$GAm\O>%d3^!(ZeL`T%MdZ2OMs(?*I+S\n$j %pl+l^Zf%4"s&L'&F#gUf+$9!U,]6T(=?t\2m3N1&646Ai.NR6BeJ&/+`2>"3rU%--jc/Z4cO)5arX0*iQ#pm)XK6bI&=&b(.nREr\LSTo%);BM7QZ_O9Xjq5=f[MrL-fG?QY1)M9 %Dna-DgZ.R6.FJcOq&gJd@>4;=*Z)Fm*A7.gSVr/:l+21N?crGu>*dU@D&5oJgB$BdJh3jcHL'5687`P*>op5'SQTkhF&U/8]N;_\ %&Ebe//?KuIZ2tUu]fiIJb%!ZHLM"f4:_JUX5CS:J>A/ai;etT05=56R+j/99_qON3Q.!YFKG'p=6UBDB8QM\@l:H$/YH)m6WX!5i %953&ld_=97bdE3TW0M,nd+(ZudGPd>g2`BteJ.SVL>->;*Tj3%[O76,CoGnX9kpf(T65[]bt9/:7U,c3,IAf?;K9!GgXshCRh;2J %Gn9.%2WRo)/_+GL28Xn]We5)Op%Q?!PXQUX3dKp6pMdL!PtA!8p9?86S@t%7_kj?]pTYh!M#4C1h4\h?CROt#?>^_O)ELu;YIb6K %_":o\XlG.">Rs?Z86N;f8F7W3E+0b/;-[k,^(5^HU[E^R:gBL-9XTgH'/48G/m_W'W*JVYKVR5SSLOK`Q=9OAo>s7HfO39C7@:1/G?4[T*u3Kj@ikr>].%7F8g!@!BC4U_/m?ia0.C,K&WY/c9qF_Wn:S7nL,.V6 %85UG<_&"(D4R&#/^iu>kEp$K*1NX7U8D:#,nWEFsb;qml/#7cVeqmlanH_]b?R8knYcYWbbC.o-hL"E:UFjR/ZA;+4dUO$%J8rqK %0!r%_DUniS[OR!fkB'S58L(Ih!%TI#X_mE;V!P=N^lLDm[h>@h]<)4*,pg(Y:8P6"d+18Mdm<<..&hNXcp+pSk`*q[mE3`XVf)8W9 %$V`O=@a3"F6oTgOXAbB+$io]"eo\^I2UKF(mR$MC3Job0f.SL%`)Q!/1o-Q\rfd;#L;fC"mR6J^VK]l[mKmnSiaf-\>e %QaYJ=T+7;IPh4rXU%oaui)FIeYoeIKAq$sp%HiK0VGNAnAHus`BY)#(]XIt6ABiUP[>'-r*h"sM6G([RoWsV*g/Q@L;lR0b %rN&M&hlCRm/EdPn%q@'G@"op_6'[qp,19$p/:74/_B'g]V+gFm8hYI?<0LoD2Pn^G8N9&rdFPhphYR.NAo>3"Ia.+\2*TeVAuiq5RlYs>WJ@6cXA""_lXLsn %ZnB)O@Wh7g7m=fK1"Em^@)Q23GU_D>m#4..-gF';E#YSO?LkJ0hLaV6)>iXUBH&Ico/G6U)rS49No4Aho$Blj_VqU1:&7LW&>L]7 %-&(qqjV^HKR=[JbWJP$P$:.![l@bjld)46h:I+BVB)Q %/]tZ@;u4QuPh\N$4J]tXp95U2JAg?8mS7+prN5tah1tHJp#bl/\">k*pZo<'J+l6%IX^fg^\RBXc,8<)0!s\KrRkm_G!RlNL)g.^ %?2o/dWT[@.7K;D?qrcu'LNm%Imf9"#\IFoP9pmS>)k#:CQ=l0P1hu3k2=>+R&LVLl:)IM8TMapc"%UiFD %j@CK=-^8UII?eT7i:)Oe"QPG&j?4IZ7)+1Vl@")d,Mooh5m9B<`pT\/YP^dILOCb'jtdcC-Z8E4,h%A3;j*S/`S$9.UHn]I:M.mn.KCd>DLE3cn]b+0)Cj;rb*aM!D%cY7tWDn,HZO/_)nf(Si4o;mG' %:L7#D;El.C6PTVi7tIFP>hR_7kKBk.DiAA<&'VOU1a\ZqbM!j*Gac_jAZRdg[V,I[Rh>eIT)3!=g*"AgJ$5N:C$9+Zig9Tfmt]ZNSOYm(;!""[k %eIL*&pUU*AcjiQ-:kbQiFhQV*W02Ne8@L(QRTe:L.auDe10t^?>L[]2B*5/&L/Y/2B0oFWHhb]*pEG'8O\Ht)?L:#S:X$TEiLh-M %G+e9BPcq'q@Na!5`TW)s%b)+GX%GIC6.OsE'3!/RU_];GBIthg.`a0S`RX=XW#_fr4T#>dH3jhke.0t_k6oR0e(7A#AT5!YFk"Ud %eCuR4.Z&5!FK=r3WF\f)mVGS!U0(pGAa6a5ZN7HUA'a9Ajj=QS7fTB;gl(t'77*(P'7sKO4"^XfT60;gEYJR2<,1"@m^1QI+had$%I/NJSD/[S_TeT3IaR"q]_0d&_/70tO^326QgkI)&+%bo'l!5:BMkF5V/h!?]q!%ON %o,S`;8LfuOKG(kM\%mqCQ/ufF(LE")0.iU#i+nID"4ku9DQU$;#DH@SiJ:saW7W$jbDEoZk^71]b"CQik9E3MuRAGA#E6WW`$9:2UeX"<2H\Y>It$&aDC.Y_5ReF'oQrg/"m7P%:]7rU($Ln&mJVU %]lP/dphB&M/RS6YN@6sBS(J59fRf'/8$%,uXkL*.`D%tM#%Fo5K3$rEO_L%h2uCnr/RMeeE7F!oXmbTAA1e4e[-IcOSZ?Y2Sl:?l %39i/bK.t=rQ,QdJS\cg0@n1,+l?\.oJVU'JZgFM;*,B:rQWE&ZIjT?G(,k/N0To'Mb\H)e-ulf..i;-Z(H?XbNtca-p&J %fdNAbHj,"eI(#;qn5RjF+`Km6mEu %Eff)&0#H`4GQ,c5DY_,Qr1;p42-ELfIh1YQp*d2>*_KK\mCjY+H(rCJUbjQs"_(^J%MAF#pkqlgII`_ONo %lX_X!DX?PkS$Cb_O6*0[I6ohGSqfjgm.jN1qTS7ojtb+78bZ!N+`DB*8Y0^ljAdNJ$7]*lksL$pp%8?K\b07OLK^@e!2[:,p&s0r %3=YnFGs'A@kAR`*V1@U_M8h#(>R7>?U,0ZtU_[%WSrXm*2U0-XM_^McgMXla"4B]N7\kuIIEdNLLJ/'ODbiZ6)7G]J'#:EqlVl4P %1uQBBDi[PW"acSk;`,c:ci&_A-huYH3H[bMfbcaVR*g2tnOQ0S<:[>=43h3@T8B&oGb_,'S8?i/5QK %%ET2MJ571q-ZgEW56@HbR$'WU\O!e3="BPQgY4]kic)is1Knn3oqRp#+0s6GeK6X+bX,qEM2&a<.>;LtX;MMkb076O5 %C+7bYT[kXf-]4tODPnrFB1SBp:RE- %fMqPMUK(pC*ud)cMP'-qDT#M:[)C8b3Q&nk2J>lceP)[9H>+;O9PjU8Z5*sdg>Y3B3i]7#\[$5K!2>*>;BNoS;K.,>#mr@Kb4I*$'-s:&,?7p8jZ6-cj4J-Z"'QUe:gCJt=F)RDLX! %[;ChId(h&)6GpUe[c+R/kX8-bl,jFtVX7%c>(tNWor7f9Z5/J.SWu?QANVn/9k,%2&!&DX`lQT:@JEeP4e[d<@]XE:i.,17m7%7BF[\?Z_T[C&<=h264lTWB'=&hP1u=i7TaDeI.MrJd=0cP[`pIqZ2f9>O;[9spNe]!ea>hD/f"rJ\nPd!S(:gqt@b4r*/DPf?CDC0F4Ein.%PR(_FAJ6d"X]La_:?EA^Rbp!e&,n^%R0;:4rk"l#)jUa.^e2p %IbaC&erW-9+sT4B#Z%r5PI5^F_n!TkGamaal!2fW)PEB8>6mGrT5m@/?U:\^$t7"cn;GI#mHb)7-9^bYe.@5JT]!1.;*q',CrVD"_r:ARHF@R0>Do&V6?krfe)rS0'Q[G:%d$ %Z9ZV:^;`j2_(P1G]'X0WqiFg'W#=>bJMrZFZ&g;UeANDV_JFY?h46OE1Q0Z#Scos7??LQp)+JGkVk)-tEF$D8fC&+Iao62Ufu8j3md-(nEA\YP6'hZKYb#1-b<,Fod$ACd %3cglFeb3?GqA`47BE!Q&3Y6X4``\r:m>*epcd$`TYDZhdAWA$]d@PP[,P**pq=UPm`c-?R&os$1H:5Zn>*sR0m:0]Zd':?CaE_U, %pA5FBU@A507;/pTMc=UWrU>h2h]M-;T %M2q0@I5WTs'.)f_d;iZijl/\'c-!mkbIr-#ia`&KA,sJW!5`7H`r9;FjkN8Kq2%_5Q+;aC)[@K["s?VE+AN+`:'P+mn&:/:9GCd& %.(*2\QmfPt%_t7GJ:00QGqoAF1&tg6#@/-X\h6bMg8ljLFPTL!Br=)@Fb3qtfHs%4U?hAcF^:U&\e\n_PpU'fU?dhq]M-qrM69Jb %]f%C!'3uCrd<0`/lWP4_K`>pF!+UL3eoc84lWTri1&t`V!tZg^&17Q6p@i$%QRBAI"e$e@ %O=(,ap@h`r(FQdH:%[/@/5C,j!uAGK'7<8^YJlD2Jn[RDhn73Am;(B9#DN<:^^P %R>X"FYi?>G4@_:-78R:A,Hdm03Ic-8,!5L"A7,gp]aijQEn#t^36LBZkd`ZP/$T"+,-Yr/Z-Tt2Z`)P.DUZbpQXMtq-!I!jc/OB>K[t0A]=hQXMs:R]YX7BDp+"#;aE.Q/"%1 %[`DUA8a^Z>iloe6oQZJQ0>i$Vmomh5fY'R).=9?Gm*#Ue2#/ocD:&eKFGj-Q.t#XUlAUEM6[3\o>5@Q)M_g$!rnu!@ktHgb6b!D, %MfE%t>O;&$atjRnd-&eqGSKCTdpj5/8WPVCjqq37,N/GR-uOLZ0s:(/.#TVSPR#Hne%`+s^9nqc7#e$32N1UNqXNXC.k-71RRuE?!N5Ma@9a9e.GbgfX`+f>`7Ba,("e2HSZ8T'Dp34l#%3.Tbj(F?dL9$D %6&H=H@uj,3f.q94Ec9+7)?7]?%G5If>ganD-2e75E*`CeO)eNX5(1N8..@'!mP5XVHt^[0?X(KR=C%?&rHcm6b5+I2Q8#M??agG,of)eSOl %8Zc#WbkNsE.N4*U5Lp^7SIN@Bph@u"\dnju6r@U-Td=h^ApE(CMIbsJ#2A4YMG8r:k`0fPIG37!l<";Liqeo/ %O&ZBXHW"1k7KrNiW>r-jW'rVgBt[blUOf=*):t[@JgN.33N-3?"c^tH>(G06RHuU.Kg0"HhJJF %A#4dEEiKs$/N:^B"l^2J2V'lr4..cU@&U%=*m&g\%(1,)39fEaX+g4jo#5FM[\dg+HY^&l=b2?nAt>;&@g+_[XM$3j/W?s[M!%=P %BXLh$oTD3p@a`s]2@%GI&I:m9qqm>-5kB))kN:H"7gp#E^GJ*_jThk64FDjN15\bH6H5:S78L:/7E`hiaM\WE#c][aI/=6S,nZT@ %"Yit1$fibY*U=\A*6JOKji"*fl/%s8.W0N9ROM9TERa@^9PofQBM>+kbF3 %2(>mf)rf04h7'+o>E$T`"YGkDUX_&!:oiU_=Wa]'BTAn$c8IZgU1F6[fg-M"PAknt3)P`SA!tt'S(/\\:\`?9DWZLBfYgr1C)E>Y %UqH_U^hpsTkDg$5rrQo+2LPWi*6S&o+nAulm9=AZ?^4op29uk\G3AqU>OaK#tGCcK`)3H)srG%i)aF20,Xj(AcpiVY2WT[teUUO9I-:^Ls]PHjdnA;=ret-RJ*qXB*rpcA&pK8Y,oS7"eg*tF3laRST\B2 %Yot%O%&aps)ZH!pJu[YCP4TRGm!`B9WE$3?B`H&@fe3lWIXcZSe*n+Loj=qiB4Fgln-QuqD3&P?5K9Fqn9f&ArJZ0o-Y;Ja2m?X+ %*9n9UC95=c?L8`tA8GVcJc5.9lh@=AY2:`fk[1qBpZ+lYKI]i,pXhD"Et\KhfiNW:.-r6YD)-8fhHt_GAG#cJI"V#Jqr_=jmJ*L8 %]RVY2DXYo\ZCd9E^Y#FG/#!,YW1)D"ibS6G?P@m(Qa(F,l)"9dYMSkr;J][VRR^5E0CEK>MOl]cs!$3dYAiuI[\M=sQWaI=TDXqfrTWq2^\KNIrPMDQnr(6-0(RelTBBDNQXRAblH^S3Y8d9.S@b4U?'"XFUNB[^h.MMUI;ImJ %@^YaOS=j7sR^]-[Rg;9%La)#c_>^"/o:]((c^oL]fM\kbd %nT1`:J=g%GKRobVBAqh&YmZ60gr*QK_"XI;C&G;%rO]iEkCNWh1,Cs5ApWj3ApYYtlTDdH8 %rqbqF]`7Ep&('>>6&h;[d?N+FZcmZH\HRV*]>79h?5[:OrcgRJ4 %IsJCbZO1-eD"L[+s+k/`Ip%CSUj@HROIdN&ZD2?a-I*PNY'Y%/rigDdH %D9R\gSiq-pk;/U[`O:3/rVG!$&?FDpGd^],=0k(pk)Sd3:ThCAanB&U26cdj)OuI9f_LgeYg65/ZMjFgs*ouDcTDACj1s6^'tqmB %cOGN?qp`D^%E#3l[r[m1I.,9js*t#^e^.EISsSWDU(*5Tbot>G2b[($^5uA*PpV0d=^cLfekms[mCCE,n`lV=OiH%\?WQmL_VR.R %lmTK^Z\3s$Bn)`I-+^iOek127B2g)a<]Gn(`$lR6c0cFpp$!5srr>6o5EQuMqJBu3bO`i,8r`Wj`cF4/$mG4u34/$/%h>.'.0#@2NsRe9'S %0'.c>H:E$7%`$+BoLE,WVHBqArP(*?qRP?6W@XRak2-\PD:K/s7iSc\pQtkmJ*_^%Ze\=+&C*-sDR8NgI]i6rD75GW0ui`d#J#&c %g[`.d26@k8BC@GY+4G"ZY`6SXG[<9q!IuqA`edi+++Rs5k=]5bDS9NWI.)4DB,jGDtO3$M:Eb %Pp_i4\6k9j+*B2a)9+X(dHhF2R5)FYJVUJ[8`M27nj4fo1n4,U`kAqss[-d$ASE2hkOUuHG/9?h-'anLIiV$AmcX"hMhA_LG7pPr,ii+L]4PN?QnBM'n(gNu@p!Wo$I='XP %(!gbTOCqg?q7/A'itbAn-Y[JH;YO[>9g1XNa0TLB$K:C^hjWoUM?]Y.T]a:,j4&C.O2ea+Z8<[T]s:iG.jW&=ijJIG>hm3\#e*R& %ZJmWb`/bhT0kc8(W3`/+Fe9^K/R2N)lp2[TKnd!nmi$RsL3@,qJ3L+2FHit)[:*\0T'U3`RjZ9:+8,O"?)SuKQ4]alsh4PCsK %AarEc's%W5@KXr!(Xk7E4]XW_)"Cq6=)*?\CuOB"'NP^pRIm^E+!$8GN5c$#9NCTm(jZii7Va)o%>)\`$='LB<&rI %r'rkB\_;bA;jDR])8`]BV&?<7>ksH+&f]aHhEj#1Nnl/N]\)"9LnEFJk/bg^UK%qafXbhrkV!LYEX$r=TRfVG1u.G53SoH19nffG %*j9jn#EO*t,-u\aF/?^+F6dD`UA4EuG]q]_gDMnM..;"\5G["T34Mk9GH[/bN-dG^,XY)au@Ad;;s %](p;b]D.QfoqrlpqIQ-\I/b"2rUR"<3!()QkB*]nk]<0ZeuqS2Ga+f)\`-8AgFDR>2*'BO#!lI0L9'7Q*Va*T^7d %bFa6n&AMJU5?Qc<]7"*@dC+PKngN9L;GJ1JK=$ZhYRF@@bs3eBJ*42<'X2cRebniNimLC1uE_[30o41*>9EHqfK. %HT^J;S3,PS=n,cBc27/;D>GA-]Y*=l042/B %W;C'65-f5koKdioC%8G;HuINuEP1$8M(mr+rF*sS0ERl4f#I17>N];oZbga]A@(j`==F_[',hd5iD&ujV18Aem-*e.;VWnBqVqt+ %3%@G.M#*dM^:MPsoS[Ts*5Cl=DgLN>?jXQgoR\.e-E.=+nfu4:S"e %;laMXD5u$g`iO:CKH9`,(^93WKSih_&dA##RuO;fA*VO(na(Q!hV=hE:7#T`L6>Et#e"%[$OXA4%@5OCN!=BjfnU:j,XMU%OIArT %(K^qn6O5^;WM3ZS_Es'Be].je9B+'.rhV2(.m67]+f:]<%HJAYHu-;\NR#fRRlE=Q2MWX<*loC0VWY'=HIGnsL*j:^jk$,Ro^%WB %rBUt.m-U.sp!jmA*Qo%8YplS8h[6++TCm"73Y/oZiT*V<'p8t=do0:`)]+5r0PQ0.:nQ+9Fi9kK)/.FG?W7tNBca#RG(+2g_FLm-\2-CsI9*Z7X?f*1Q]X_DE1c %@\l"men8On!mfFcV1ra%jmGiL\Fql$>FmVaLQB]m[JLBa$j/H1)s9Xek<'2E=*rGLN">gH;>JC"R^^;^3chsLp0nDF8>)A&TnJ_)`XlMfuCAN&7o&A&[sN+Y6cBbg7O2knAOEisa8+7s+W8r9gaQ`4#]n+n'@F%hC(DY'L(OhOd:3IbGnBB;m5W[I>%%e$;G^=h-e)^1PEdt\nn-b[ThPIi5-0_ %gumAI-\Th9!PS0RR>MpP=GD_G2DcS!PXi5RAZKs..Qia+`Q!^_*/iq.NhMr9*P'g&,*(lmUomPMGE(46\3m$JQ1h\J@rDjYH="'^ %70ASheChI[%H`K`T]?H&bqaU1hk.W;-U&@[Tg8)cq4[aqo@(RTK2]Da;0?h5cb<\)auB#:QIQ0iUO\8k^n*Ea)MB![QO^Q);TQBC]GPdX0oo=0V:Goe&n)c %bBIp'g6KsOh"]ShrR?Pd6%Ch?-1mpsY_IW,HlPB%!iR8IAR_RTp2]+C'JB12>eDlTbKZhAHs!BV@YV!#m[mT&,ua#2=pXHJC6p*2 %(=87(^()[[Z*YFpOKEZp=ASj&;[!rq6Rtlj3&ZepJRW@`HpNZcJ$m#$mU(^2-S@&.'@3As\%0]!Gkm=>q=V'qX%2r1,6%iW&?K)7 %OpguZM//'&lcu3t[Ec"PC"`"3R]rrDC!L-m5JpP\KCk4Q*g`(IQNI#>J39,Wqd@^EE*5$(1YiN4#_uXpc/qX@(u0CtcnQF.C_%9N %&c-p.\F[dV^k6%bXR3/LLfZj,m"9YP@BBodO;Rlg-Th9P*kQ'sC[FFXl=bBp_&fP._RD\+<(SbTTD^M7[[AMTcoAV/V?c_TB(Tb9 %<&&6%^_Nj)t=&t631]SX.NSe]qr/([Z%Kj8tf2E7G4T@!!RIeX?9i'YXe"F$WX&D4k`,:"KKE0_*-g] %&#+diL:dgC)e`q$23:;D-b(hhmHa"^Mg;Y6l3WeE2Z*<@ig-:P\9%(4im"A5^[`^,L$#uGZJG!4KkWf-1I^'HGeeb#Qf?8*6QS^_=BSlWhS\9Ur56qnE*]'r1_Dc@(VmlUKV9 %Ac^NHpIW3s^/DI1?XLj;VM9im]AH!F4DJg;52sjTTlTYIRuOJhr:-=*79r.p=T]/G&&0pofHWfNb?W84rnsS<\'fk`HAp-:/Srjh %=@G=Xo?+rO46uVs:aaFO=YW9>!u:Z%&#WM8!W.7tdbl_Ni7b[O^iM-4#;rrl`<5sKX2P.l0nOTBE<'Ar5@Pb7i`tUH4:sZZYqG![ %mkMWE6*D:birOe),>tfb.07*(KJQ^'j53!9":I?4T>IR\!L562AP's:,iKK)TO,/%6'Mdt3 %<`1pVXjkU'rs*",Qa=NH)2]\UX#Gh>YMN*bKe$E)FSO]&RiS9ZfE(o$&`Q#H$10Drp-n):=i?gbVDe)9Y9FgaKnOVpI(8f+R<[I?Fk!N*jA9b$)kc+2aNERuERW"VKe"?iKoGRJX\.(Wt3 %'Eh6:k.=lp%N1=m)CfK,DY(T55$j[SZ[01omLElUVg("H2bW+cH%7L_d`19A(>7m91*qkE\Fd);^-_QL@G[46/!)$[4lAb9_O2J& %2h7*70l@<8ge[A.;C8"FOQiYjZVK!/"`Y[)C:%tV1UDY_IsWrRB7&ZT(9Cd,mC+`5i95V11RA0p5hut=2@jS7l@-\?-RAb[6F[l^ %EmQu\J6E[9>$H=4VU'kAIs]_eM#R00`!30o,XXuh<1q*;0dSe!?9[=q)gp3(A'%a.<[M=Eh0]DhRuV+8JLkIAZFDSD#/8MY+eAgk %?kYHREm]Rq1h7^*`FZJgC@;Gf^I&ERV!4L`YJ0O@>UJUWLh1&Z]Y\>+Y.U*%q781cOf>-*+%s.8;a#ljOm;B^H/1*UNX@(NeA%Q;Xe49fR+WXm$O5`F3A/8aF#a3V!kE0,+go#&3)S.=.RJu_Z %jF5j.2MI^Vb^e#AH,'hmUGuhC:G.-Ggd.onR:O1..ui,33RR(BEs7pU5b0B[#(qsGVj@4DiW;$?,bgk?S&e)M=_hL6mg(SbB[P1j %_2uPoGN"pU>-lhkJ2cqX>j5N>lI[_Sc'`^^8b^9bd19d&X(?G>'0:9DdA_dE)`KCq13jWj*,FiBgp@ue/dPFe#LSc30/\A]pAJ,=Bn[V)*+an]d&,tpe!1lLDKNm7G*^Fg;gL8S_ %Z)Y#90FXGI]/6HN^'!WBbd1;uDlqls7Bua5V.Wim0LaJd6[gtce_4c:C*j(1B^`?G3\1JB,0.dlPgoJ&^'4Vr^U31f-b`i/#kYi":Nmt9Tn-Qo*4PbV>B$(\OZPpOqmSt_QCb;8eKW"LY)"h)A[<]i&K@gV0F/9I!1[!l.PgM=+Ttj:[0(&6#gUMB %^#G:7nhMrnXU?!I;h@nE4Or54'fW?E]_'Hl_d."DUj?Y)$@aaR?hOjpBpg!@lIV %`0;HikRa0<#Lf4,mP:II<-*`HpX0)f>[seCH-FM5jTTp7P(N:n_:%j);5Zn!&D:ZedX_6%Io]#m4:_@rVO,9pnO3s`f06T9Q2iCL %L#FR!5d2LdnI2?)ID\00u)S!b/,dJd[Q&/UHBBmN`F.gU7J>B0aVDS8>1Xe.Z1\!*nPe(MM6RiRPFM>eiPlt\#OJ/dd<*'$qg26[9\(gl_KqG75?\0VJ""E[-0X9F74DYNR&)5@*ic*>!B*P(Mk.BAd*kN)s %hgq0L]/jAuHUGi&=nCf_Wg/VAA]H.dK3IKWnmdg6$DSO51sRZ8+JY%AXp>`(6*'L_C(Dn;Thj1eG"haIt`,)#St %1"q.@8Ipm9O0^I:-]AOInIEfXW?!SY&5EP!;"^l_?sp2u93Mlrf=U':PcN %Wp^S(9'%o-6S@0cUDdpb.G_F(BkhY(6TGT?T*\Gm`YtoLM]UZ+7R,Y(5i,s,EDV9a^qfX7_"eKjGLCaQDo&88!,%1BG1A7I6DHYm.GlLtF7WDhX9oQ/*'@q^Ohh$?mFMXblLOT8#]:lfQ3m8QR#V4KEA3hbA %>oBr."S37Y&].%ep5G;5J0OCQJB+NFiP'<[5T!WRmQ\u>#k;QRWF:mlD,?@V[+Vc-jUIcFKZ5]R@&T\W7Lj.oAXC04C %-^>WsWO%4WAl9_X+/dsNWH77\9Er5t=j]#t,@$1b)K,`%HXgH_A1[C%j*,mfBbrh%1S>Ep>0o###fdY8-aa.#K1laj`0]l=+s'(V %+DlPmML[(pr;5lW=srODPueYNtDa %(>,PX_$*kn#42]iA:\lnctuCbX#QQFqLrmY8`:BSc@-sjqf76WcU:=cW>jWoq4sH)APS^kH+4"4(`QAJg>m>N.iKfjDk;q>;Cmm8 %U4K/:erMs:P+i,C=l2oc/HNsT%e-)8(:G&G"N&FD@iV"+s77^Pghui)Tk"k9'$fV!Wt^jFR^n%aGj-e5OJXS&),Pm.MW+,p_MT6d %-8H(.YbJ,=d5Zb(krTg9[=KTW)G"TgW\EBAP^7A6+$7q+I;0mG(X)br]!WB1H2RfISL&nrKl>liiY__*BD>3^Eh_H("m"c(%]q%, %iX.pQKSek"ENH?,po[JL[PG[2_,3^eio592KKYHLNr2)?1#Qujr&s>QE$#_hPhJQn8$[p"LdRdWIV/cTF)S>$fZV,ZiD_ud8cA8b %kL3>E0t^pa`"UQg+RZ-@'[>Oa0=o'q+a*b9AlAdRhg\)\?hT6FIKF6,Mc=CPM]@"[]h44cE(Rrc^q^9t-?C234$rOkG6Y^H %(;^!o5\u9qK3KJmEC+Z\,&qP8TihrmqhDr/;hFN`RK0[P"h$fm>SFXf/?mZL3t\:?5k![K?50S5ZB[R%Fl6tAAM0P_2I4LN;7@D= %9[DpdY"9>)%Zi+"fi&SLlZh'%^1.F)2&FkZ3ARDP>B4`Z&5Z)II`)GJp@'R-J-uVjKC6UMI0OLc@EZdoRsYuShk,pI@AI);^a;fr %(gdIn@AT@J(`J"d;XBV))Z[!a^`>bWO+@SQb8MP0I]>iK.?]6,DTNH/@cF;:JJr"p8O/#Nc^CIJM7t2/g/=OY?'DW0/4F8S!opj? %Pu>98F/>8b71`3#PWaJd#ef6>1M&&rHE-UiBOhB]D1/2BuM'`cV3=e?=ns-J$7.V?eU"p%tP4^B,ZNh'=Fg5=ctI()18f %^?-<#MLJPkjfGeJ^o.[,Yc:I>#<5C_Ukuc_L?]*[bmNf3W.T5!q(bk#X2kYga;IRlroen2fYM$,F!s`VaeH%Q9:DJRkIe#kE#i`!1*/@p,.1_K,f^ofZ<%LkBg[[V90AC$n5$>-+ %>RY80M=VT>:fnGU>'q.p?<(Roe/hAKBE3!P'B57TOgb:rg\au;mV[2+O?1?3EDS_WK'[OZ8Z;rL>iW8$"$712%SsTU"sR3_8ggTD %_"ntB9/b_,lBiQYjgAI0_LVh\DrBAiL%+9Wiip](#%n5*(RtpeaVhCo`HYmBj\CH<1Im!3?9L;[='qF^K(T2:em)UUMZ[naSOiiG?qH$AOB/bF8VTSdC(gP/C?#_>;En;dV8eMpeDl[4?; %JY++BN9p#TA]1+'ZB]kCkcJ'f"sm)h!dOpk\O5[9i!->V;=$AGVVu$jl)KG0iuR@ZL:JP2i-FtnIm@!7V,o? %3k];YSm$%kfq]:u%q'---5PI,40/jeJs8NHKTI'b-+n;8QdER$2(h5OG\/RUXGb@2O8a0\Pnguk4$2kXq_/FFNm5CH4,Dco[,1296$iY*Yc5F#!e@VH%CTZo^PWjN47I %Y#ksmrqr%JRa14d!R_WDX+M\a`X@BWpG.QtE-:Oi\kVgBoh<-Y0Jd*RWZ*+as'a/fZ75FV$`;e"?nb2iPQY``;iVR"56lL %^rRXsH>VRMo+jK"W1K0&=.9DIRaK1+.H*)N7/JOZ,\UR>1Z?G32Bd!6nq]FJTi;)6U>6g"A>-c`I)JNH8tnas%NfMA7c$epUH'l=7cSZ)DB'HZ=piTB_nXL:`;!u!_nJ=]mT?7\AOQc`^,8r&?j(=_F_Ei5)oq>),a/_JsgN=SDDss"U,F\-XN`^1W4uRoQ'sS^GZ/3GjeM.W(sU]d:C6\;6MF3Ni4"cD=3*UV4>pgG7k]OG[BijD)(A1OUjRHXsu=)AA_N2F*X>Q"cGf(,GGPdAMMPn\/W<'uUA8 %!:D!ml$nWpV\F.?&?=Sf@b$Bji^XQ')H!Xbs!=7*dXmr=iHn,i^1["88f'"jhopXSZCksQ)7;Q<<>WntT$u<[Z%Uc_Za'^LgI17t %eREIV"_oT1Wk#L4Zdag-]KppM0M!TI+eXD#])^["*F1XL%4h`$S*MT!3@M&ht=Y$f)QnPX%[fcgmg.4;CECSjh*BX.*?(>PR$9W5\&K]mG=3:SPaZgY20u!b"k$8A_K)cQ? %iLes,LReoi2]'@'LOdY#M&j2nW^lG$j_GH6;Z[+?INBZ8Q,<VJER\SQ*gO-ddJfAG+1'#"b>>cAnd %911sZF0S9(f5YC)]KfAJV;JMi];Y(Y>j]R$-fnql.!.GcqmV0=1n>;3U^--L>t5$$6Du,('Vh_o%JT'DgnI@R?JF''&*,Yn.j;Ig[)VE@g/VJWOA@lPID3a>(N;G^, %_/hjnH)_K-MOAEY,'l]i\%,NNl!oJs!:;DM`@=aHNrm\<]g3o[+0ZXJ?Ir1Qb45E<;$d/$Wp5A,L^B3.mD;Eu!]SHGofA?DJ]K7C %<0A,o0H_.Y+Ljk\EEBMsSUq?c?5AGb=lSE[01p`rBC'!\4b\FR5n0JU@t&m/6Uff^?l^SdnMeqDqR+FZ$B?]+joj %e7p?sFR7h[2!97t`=,r?;7_Weafmr2<;'ngH+XRNJY`cp&4[0"o$gLeW#`5S/X,Gco:[.ogXLcRCXtHgirEVLgD[X;cc%r.P,sdZ %5F!JL,,m&FViJVo8,7oXj>k_&>6o$9ChArh7T*1N/P1\6P"u.)_`?2?d)B]W@;Gs6Fo#Q.cl)L:[$d1B*6Xg5YT=oWNDkNc6HmmFm6@r!JYkm29\S^5k;t^7%X4f %S![d,I5_ei;/OC#];e6/M@imZ1D<\UWT!^Ig)mh@QLKFl?j>WIk8Y9*K]h%>R8ZYqB/1%\gZ)2%#ffkA %\-[@5K3;)sVD8>A@l[@-s"(5p01W^S2^PaE8BkI\g-nf(<,r/CrJX\X9R,iEFa4Sqc0?bNC`1]gF@OhU-`XZeD.sm=QXZn8?:b@u %f#5f+BOMptYCp`iI_K["3bF;k,^iKS6TBY0)LrpVl^(Rs&M)#Rp5&:d@g@o^BrM%.W]^I,Bbt[LOH-LdR-$k+U#%9^]*U!K %c9k@p,=%\*\XaJMR.Zuuq\PjPLHEQSFl?PqQ&.Bt,S6auUe$16QZBBgog0fH_H];BMTb&\B[6g=*+"8^)18Jop?(R0r'R?E[oE?uCtQQZ3,]$8qb5GSVbs+dK:R,3J.^''d8?9][HN8b^J0c`?j1P& %).rRd)*u>5k:PGgVKPQLmSQP',#HKPmj_f?,Dq.+_lBbhLd_=PNre6a/[>V="J#C+i7)4V1ZAl61(\DO0(^,RS=$.UnjjgY;tU/X %)Y;(kDY.D,OGNZ*6l9POf:81*CcZ*+&Mo7bjZr)$8]n6ZO"hdja_F5)3KQqaCUAHU %=e`hF+mtCi-CLH0`(O_PAVtB-Ng?rF/?B>8\:eXg626k7E.lB.&pBVf8F4UAr\m*i5u`r^aNNR>^5n^+oB_Lpk+Tq_f0"YJ2DZRe^baG/p?CFCCD)B#1,_?-q0-^sIiO(,glt00UFQE#1hoeq6icq`8N!Un@IG^a %0K`Pgk'Yo(4F$]&Wc#S4*:+!o)m.B-6cf@<=;e-@is1'mlP^b/PNZL&4a#6h`WI79U]IBjA5a0@gNaG/X\e4*247FO4Kc&8L]"J] %4\k1&\jVNa';LP!9[T&7ih"6! %_n;:uUc):/"D7bBp^-9f/[%NqT9XFRMbp>&f4/P'Rr4j&Bk?f%h4tgX75Q._(r55ICp?D'0fg+a;@[uS&GAO^"6Pl8isPBt=^7R: %uaX8bMeQ0-QM7O+9]3cbN9j!cn'#il*.c`Q]?(L-/)%L"j2Mc>[gIL\jmQ(Kg%@lT%5c/Nr"4BL_lHO=e'?; %]E=BWZ[d5-40T4dnVmAE5[JDdgl`40h[GaT8r,&C@.*\"b.+ZD[V]5^J>K3a>J!28>H[XoAsI[XFW_najDSqAm2H@!Rrh)[#I4S" %ZEg&L3Ci=#-MY-)#;L['!-ooUf=k7Q2C=`E5uZ6c[HTXY?t[SZh!0ljc:iVt<0WPGA/OFhpa"J#;YLU^$pRdLX8EE9PYSJpWuOh% %0(0/`(G(?pn1_p#e63USN\P!!#3+4?7uX,V^A0\4/IgLOI['c5MJ5&BIE4 %eJNLg_amY3:eH("nmfO-B%U(nl'8h@)OO4XFOP4aT+,br %6alC!7QT"6.8Q]:bQCrAH_a3VG..@4B;1*PKth8+D'8(0LdK,J/3<9g4$2.Bhu[HsR&IJ@mhR"?&:Zt_*+D0pB2cV_9*),O!^[&D %lgj_>)L,$[-[Mp7qTpCD+PV0jRp;XJ;/3ZJ,WQGo?!]N<%U9ImK3&FY@61CPXE4*nn``I:MCCN2992LN2eNEn\R@9^V#;pdG-!,J %QK+bF..nmWl^^g.QCY#%c\gfLE1GS6n]3UrHE)t]#`?E@!QtE9ILnm+M936U&XWr^#qj#N";U&,/=_3N*@]g2\3a,kMZ<4CT:ik?2 %8/kR\%r9W=0t8!eI^rj&A2,?4:*qk(W/pcO).fe;OF&Yf(9'p4*<,GU9se:dPH9s#K;,*TbZ(e&,O' %"55aXrs)2;QuLL$E@P]??bV,jfG9dQ]%&<1>:c($^TdRi'u]!B3@),cX9%5lbB4AI(uaMZ%RE17Ep)h]S5c4&ilq:dG03D:n3fuc %q,dD?10j0W8>s!25"U_q>5h?7*`-#T&kLd\#jKt[Jt"'c$t\*i)Y3t;qC60`#ubr9\_'#S;\3GoE$,9bc@Jn.(Bq3gCeiNaZZQZp %SQ^,4QafF+XSgq=F]!R`ER.?C9(^iD\)9ldK_WVQ[1>@3RGaNaH5c7UMN8SDjOL:%l8JC:.W:a^`su)=>@g7'W`LU=(rF1(d^53: %=:?>u-7-4cph*DWnAI<:CTGTIc&;OrMdSLb!ed6a;i[5I1b]VZ94?3h3nhm00oqP8GRj>6*#lOcE^U]m7O8n_rBb`q2b46q7M#?% %*/!i'^X&oP^2N.n'=VO&E?3c0=jGp)b\^7iITHVU#ONc;5;2+3BDRLN5K$*^o4Mc)fhd:LW>r^bc,QOHd2e%sVFF'QBHNDsdb[KTOdEtb>1tZ#G6%\@.oF-as1eh8?0WL%)GK*$WWSl= %F4mbeGmIASmBVGJ=$\Pqb>l^7^0hP*OdCfqb"o8#0/k%@^[8Vb(6e7N8K]s#Tbj<`O!lK6)W:qJgo-?N(rCtG[NZm3AW^]dp>S1C5@%Fsgs"@3$"nl#[k:)fbL&J4PtLet %(4DeEAUu5t<3\9_W%E^:2c=_AauG'L]Y3-eXpCb/L#P5Jim1K;BsI_h4'LW8RUPm=no7:5 %bf.PqSLr"p[(DX0#3=A&b%pfN9"W1&Y+Qojq_EFEjosF[Z.p<6JT%`^7JgT$/maj1!ie!p+B#`ufP!UELJ)<_)!jSccug?$T^[!o %:OM2]ac4V=f'2`t0s#"S3T3):a;JX_?b'^m7OONZ':`:TnXBW\^]r)`c3fJbF>p2/ZS<$."AIGG@D@S%*ec=&LiD=F<;f=S.dIfU %r9NZfW]1UuN&Th%`Ee\La+mp,RI=>9=kJh;OO5LFk1Ybpr:tpX+1JX,6?mB%'8eiBI[Z#03,#>R:_ba5Qk/(`Q(KH.60QW\.1;Uo %h$"^d=GE$<-sBV]24YYLW^UZp+ASoe0&(n9QKCk@:uTAFfp,-:NWRI7P=Hlg0S-&;;6\B$OYj_Ar&XP^"oI03UBu8+5m!L"ZN9&q %X?%Or8\/-1.(Q\&2%A[48 %l>9h6T'8/@>4INEZbsao?O#,!53LFYlT(0q\nu%2qc0=VKM1JZbdNnoIDHHPaa@W?17Z*=[p*6[pu0!JpD9V+hBN\W>'Q<4=I0ZV %8cYJ@22.J@acW^3N=f,60J?lhkcQ70V\CmcQG@Z)C(lk#@QKJ#Yg06 %hn\1-_)KBcd_B=$$T;4BS03Hb]kbsriHNf[,pM9"8[2nDbgTYMUW%o&0hgp!u!IQc*_;.Z,D!\$ofj4T!"$[1jBH%,fofG6;MgJ)lTjO,lD190Tes" %j8!W7'CnY/Q0G$3oXS^EKG*Q^jq'756Ml35peBht]nG>bDjo^.J.&,qRi!)Y"[rH^-\PmoZ\>*OdR^*CD^m^" %/M]o<'t;#>C4_H\]=bDeuGF)+`=o?da24FhrjL&2^--4Fi18q=e7(e,Po-+=8Qo*>jPJ\^9MPUHZjEAXo@$>M4EoWF6$?VV-S %N]"7#b;t9VO,WV7I[RQQa+B>21jP%=PB:if\sj^DPU)-bMCF`]36VFP\KT_nof=oPX@8E@Uf\+0A*uK'J:eG_Z[!4274QBHU8Vf` %8dZmYQc;sbgk$m&Ym&aY=j4E!Q!Z49;k-]qI%9J^r`R<**=Cp*&B'_REP$5k&uYd?>gGPr*K11-gHA?#bTXtkpJO\j$"26-9Yt`6 %QpAb'Z5`Rr#c>fi?eC=q@gI>kbR7-;04):5!'8,Ni`35_h0dqL:/H-3R\u=f#gMTC@KrX?)#/IeP\Rfb[jUXK?_0>&QSl*@4C\@cc.@sFT>c#r5]N+-1L>U*N^"e-,EE^NS9UZ?s) %5;$%'<##GsWYd])N'.J9LcU[0`q4g!mnft-$BE=;,*9\eCTPd2.hCVPf;T3@=M<4<#[*)o)"!SS$\$E\)5EfRUt["Dl4UM^**5DY %\TIcY>b/Z356u[:McuInb %k(rKLiC'gm5J$Y5agG65bh`K8r"-n$&HOaEZ\[ptO,;Y*Dq9a&K7^RA!j,A%ADGA(]$<[HjF%BV:6Q1.B7tGJn+T6/iRUiZN$hW?14?;;qY02.A288W#@M %Tf\c'qb$Dc^Qfr?&CH!I&USL7T'M=V($o&RMppf=N-p>eEi^WD]#bU`[V[\PaF=F=r%-AlFARPTA)%@!X\h9!SmP&ZR %ET4gN3r6PL2%/H9Ek6M9\Eed-YVYZH:a>_"hJOtcW]Z&HIWSkpB5rlZTW6"s"uog>QLk.2c9h>h>@9n\[a-\cSq_pYVYl..Ma=nb9u:CjGIk@>BT<14,dR)J'.qJA9Eqgl1llN%3:bli+@'J8I5NBt2`3 %D".[TN]3>Tk=?o,qDccgV9$Y9)YQ@?"X>OtYQH!__UG]Cnic]BcBuMu4]3\8d/a5l7T((Ob9]?8TTM<`ctn6hRB;,o'-_\2"hHI- %!B`P*:k+1;Tu*S;"]OYI9Yh8"dT'o'`]I,<2t?%%M&Xf1V=S3Y7q/U2*>\d,?H9U)2B>7cU%&Nq@RF;2DA.jp5?b)qrT*Q8Rftkd8Me4Yq=s[]bjLVN"qV1 %-5_CQ/uU8'/f_c0!>*'>j2n./_=L(K=6KHD3r+M2?(2+>]:uR/@..hETES7K^TC[`(03_^T;iMao/TQ>YAe[TU^6 %H?Qb\R^bG).CV[59s.ieYr59:'Pn?jcI00;Tb2/4p)YcTfn5kT^9l9)0sN>ViqQ]lc=2k=]C>0sI=?ZHG +image/svg+xml + + + + + + + + + + + diff --git a/icons/tempo1.svg b/icons/tempo1.svg new file mode 100644 index 0000000..bb9aeec --- /dev/null +++ b/icons/tempo1.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/icons/tempo2.svg b/icons/tempo2.svg new file mode 100644 index 0000000..4a98310 --- /dev/null +++ b/icons/tempo2.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/icons/tempo3.svg b/icons/tempo3.svg new file mode 100644 index 0000000..bd893bd --- /dev/null +++ b/icons/tempo3.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/icons/tempo4.svg b/icons/tempo4.svg new file mode 100644 index 0000000..6fa5afa --- /dev/null +++ b/icons/tempo4.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/icons/tempo5.svg b/icons/tempo5.svg new file mode 100644 index 0000000..9500e7e --- /dev/null +++ b/icons/tempo5.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/icons/tempo6.svg b/icons/tempo6.svg new file mode 100644 index 0000000..9844fd6 --- /dev/null +++ b/icons/tempo6.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/icons/tempo7.svg b/icons/tempo7.svg new file mode 100644 index 0000000..54bed80 --- /dev/null +++ b/icons/tempo7.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/icons/tempo8.svg b/icons/tempo8.svg new file mode 100644 index 0000000..2c0154f --- /dev/null +++ b/icons/tempo8.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/kinematic.py b/kinematic.py new file mode 100644 index 0000000..d5442a2 --- /dev/null +++ b/kinematic.py @@ -0,0 +1,128 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import model +import theme + +class KinematicFrame: + def __init__(self, keyframe=None): + if keyframe: + self.assign(keyframe) + else: + self.joints = None + self.parts = None + self.middle = None + self.hsize = None + + def assign(self, keyframe): + self.joints = keyframe.joints.copy() + self.parts = keyframe.parts.copy() + self.middle = keyframe.middle + self.hsize = keyframe.sticks['HEAD'][1] + +def makeframes(): + frames = {} + + for i in model.keys: + if not i.empty(): + frames[i.x] = KinematicFrame(i) + + if not frames: + return {} + + fsecs = frames.keys() + fsecs.sort() + + # set border frames + frames[0] = frames[fsecs[0]] + frames[theme.KEYFRAMEWIDTH] = frames[fsecs[-1]] + + # now fill in frames between + fsecs = frames.keys() + fsecs.sort() + + # frame interval + fint = int(theme.KEYFRAMEWIDTH/float(theme.TOTALFRAMES)) + + for i in range(len(fsecs)): + if i == len(fsecs)-1: + continue # nothing after end + + startsecs = fsecs[i] + endsecs = fsecs[i+1] + start_frame = frames[startsecs] + end_frame = frames[endsecs] + numframes = int((endsecs-startsecs)/float(fint))-1 + + for j in range(numframes-1): # MAYBE SHOULD BE numframes + frame = frames[startsecs + ((j+1)*fint)] = KinematicFrame() + + frame.joints = _intjoints(start_frame.joints, end_frame.joints, + j+1, numframes) + frame.parts = _intparts(start_frame.parts, end_frame.parts, + j+1, numframes) + frame.middle = _intmiddle(start_frame.middle, end_frame.middle, + j+1, numframes) + frame.hsize = _inthsize(start_frame.hsize, end_frame.hsize, + j+1, numframes) + + return frames + +def _interpolate(x,x0,y0,x1,y1): + if x1-x0 == 0: + return y0 + m = float(y1-y0)/float(x1-x0) + y = y0 + ((x-x0)*m) + return y + +def _intjoints(sjoints, ejoints, count, numpoints): + # numpoints: number of points between start and end + # count: point were getting now + ijoints = {} + for jname in sjoints: + (x0,y0) = sjoints[jname] + (x1,y1) = ejoints[jname] + #print 'x0:%s,y0:%s' % (x0,y0) + #print 'x1:%s,y1:%s' % (x1,y1) + x = x0 + (count * ((x1-x0)/float(numpoints))) + y = _interpolate(x,x0,y0,x1,y1) + ijoints[jname] = (int(x),int(y)) + return ijoints + +def _intparts(sparts,eparts,count,numpoints): + iparts = {} + for pname in sparts: + x0 = sparts[pname] + x1 = eparts[pname] + if x0 == x1: + iparts[pname] = x0 + continue + x = x0 + (count * ((x1-x0)/float(numpoints))) + iparts[pname] = int(x) + return iparts + +def _intmiddle(smiddle,emiddle,count,numpoints): + (x0,y0) = smiddle + (x1,y1) = emiddle + x = x0 + (count * ((x1-x0)/float(numpoints))) + y = _interpolate(x,x0,y0,x1,y1) + return (int(x),int(y)) + +def _inthsize(shsize,ehsize,count,numpoints): + x0 = shsize + x1 = ehsize + if x0 == x1: + return x0 + x = x0 + (count * ((x1-x0)/float(numpoints))) + return int(x) diff --git a/lessons.py b/lessons.py new file mode 100644 index 0000000..82ee0f4 --- /dev/null +++ b/lessons.py @@ -0,0 +1,81 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import gtk +import locale +import logging +from glob import glob + +from sugar.activity.activity import get_bundle_path + +import theme + +logger = logging.getLogger('flipsticks') + +THEMES = [] + +class Lesson: + def __init__(self, index, filename): + self.index = index + self.name = os.path.splitext(os.path.basename(filename).lstrip( + '.-_1234567890').replace('_', ' '))[0] + self.text = file(filename, 'r').read() + + def change(self): + View.notebook.set_current_page(self.index) + +class View(gtk.EventBox): + notebook = None + + def __init__(self): + gtk.EventBox.__init__(self) + + View.notebook = gtk.Notebook() + View.notebook.props.show_border = False + View.notebook.props.show_tabs = False + self.add(View.notebook) + + for i in THEMES: + view = gtk.TextView() + view.get_buffer().set_text(i.text) + view.set_wrap_mode(gtk.WRAP_WORD) + view.set_editable(False) + + view_box = gtk.EventBox() + view_box.add(view) + view_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(theme.WHITE)) + view_box.props.border_width = 10 + + border_box = gtk.EventBox() + border_box.add(view_box) + border_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(theme.WHITE)) + + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled_window.add_with_viewport(border_box) + + View.notebook.append_page(scrolled_window) + + self.show_all() + +_lessons_dir = os.path.join(get_bundle_path(), 'lessons') +_lang = locale.getdefaultlocale()[0].split('_')[0] + +if not os.path.isdir(os.path.join(_lessons_dir, _lang)): + logger.info('Cannot find lessons for language %s, thus use en' % _lang) + _lang = 'en' + +for i, filename in enumerate(sorted(glob(os.path.join(_lessons_dir, _lang, '*')))): + THEMES.append(Lesson(i, filename)) diff --git a/lp-en/An_Overview.txt b/lessons/en/An_Overview.txt index f4f0819..f4f0819 100644 --- a/lp-en/An_Overview.txt +++ b/lessons/en/An_Overview.txt diff --git a/lp-en/Lesson_Plan_1.txt b/lessons/en/Lesson_Plan_1.txt index e0e76bb..e0e76bb 100644 --- a/lp-en/Lesson_Plan_1.txt +++ b/lessons/en/Lesson_Plan_1.txt diff --git a/lp-en/Lesson_Plan_2.txt b/lessons/en/Lesson_Plan_2.txt index c7928e6..c7928e6 100644 --- a/lp-en/Lesson_Plan_2.txt +++ b/lessons/en/Lesson_Plan_2.txt diff --git a/lp-en/Lesson_Plan_3.txt b/lessons/en/Lesson_Plan_3.txt index 2ebc4f9..2ebc4f9 100644 --- a/lp-en/Lesson_Plan_3.txt +++ b/lessons/en/Lesson_Plan_3.txt diff --git a/messenger.py b/messenger.py new file mode 100644 index 0000000..10701c5 --- /dev/null +++ b/messenger.py @@ -0,0 +1,176 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import dbus +import pickle +import cjson +import logging +from dbus.gobject_service import ExportedGObject +from dbus.service import method, signal + +from sugar.presence import presenceservice + +import model + +logger = logging.getLogger('flipsticks') + +SERVICE = 'org.freedesktop.Telepathy.Tube.Connect' +IFACE = SERVICE +PATH = '/org/freedesktop/Telepathy/Tube/Connect' + +class Slot: + def __init__(self, seqno=-1, owner=None): + self.seqno = seqno + self.owner = owner + +class Messenger(ExportedGObject): + def __init__(self, tube, initiator, view): + ExportedGObject.__init__(self, tube, PATH) + + self.initiator = initiator + self._tube = tube + self._entered = False + self._slots = [] + self._view = view + + self._view.connect('frame-changed', self._frame_changed_cb) + self._tube.watch_participants(self._participant_change_cb) + + def _participant_change_cb(self, added, removed): + if not self._entered and added: + self.me = self._tube.get_unique_name() + + for i in range(len(model.keys)): + self._slots.append(Slot()) + + if self.initiator: + self._tube.add_signal_receiver(self._ping_cb, '_ping', IFACE, + path=PATH, sender_keyword='sender') + for i in self._slots: + i.seqno = 0 + i.owner = self.me + else: + self._pong_handle = self._tube.add_signal_receiver( + self._pong_cb, '_pong', IFACE, path=PATH, + sender_keyword='sender') + self._ping() + + self._tube.add_signal_receiver(self._notify_cb, '_notify', IFACE, + path=PATH, sender_keyword='sender') + self._entered = True + + # incomers' signal to retrieve initial snapshot + @signal(IFACE, signature='') + def _ping(self): + logger.debug('send ping') + pass + + # object is ready to post snapshot to incomer + @signal(IFACE, signature='') + def _pong(self): + logger.debug('send pong') + pass + + # slot was changed + @signal(IFACE, signature='iiss') + def _notify(self, slot, seqno, sender, raw): + pass + + # the whole list of slots for incomers + @method(dbus_interface=IFACE, in_signature='', out_signature='a(iss)', + sender_keyword='sender') + def _snapshot(self, sender=None): + logger.debug('_snapshot requested from %s' % sender) + out = [] + + for i, slot in enumerate(self._slots): + out.append((slot.seqno, slot.owner, + cjson.encode(model.keys[i].collect()))) + + return out + + def _ping_cb(self, sender=None): + if sender == self.me: + return + logger.debug('_ping received from %s' % sender) + self._pong() + + def _pong_cb(self, sender=None): + if sender == self.me: + return + logger.debug('_pong sent from %s' % sender) + + # we've got source for _snapshot and don't need _pong anymore + self._tube.remove_signal_receiver(self._pong_handle) + self._pong_handle = None + + remote = self._tube.get_object(sender, PATH) + rawlist = remote._snapshot() + + logger.debug('snapshot received len=%d' % len(rawlist)) + + for i, (seqno, owner, raw) in enumerate(rawlist): + self._receive(i, seqno, owner, raw, sender) + + # we are ready to receive _snapshot requests + self._tube.add_signal_receiver(self._ping_cb, '_ping', IFACE, + path=PATH, sender_keyword='sender') + + def _notify_cb(self, slot, seqno, owner, raw, sender=None): + if sender == self.me: + return + logger.debug('_notify requested from %s' % sender) + self._receive(slot, seqno, owner, raw, sender) + + def _receive(self, slot, seqno, owner, raw, sender): + cur = self._slots[slot] + new = Slot(seqno, owner) + + logger.debug('object received slot=%s seqno=%d owner=%s from %s' + % (slot, new.seqno, new.owner, sender)) + + if cur.seqno > new.seqno: + logger.debug('trying to rewrite newer value by older one') + return + elif cur.seqno == new.seqno: + # arrived value was sent at the same time as current one + if cur.owner > new.owner: + logger.debug('current value is higher ranked then arrived') + return + if cur.owner == self.me: + # we sent current and arrived value rewrites it + logger.debug('resend current with higher seqno') + self._send(slot) + return + else: + logger.debug('just discard low rank') + return + else: + logger.debug('accept higher seqno') + + self._view.props.keyframe = (slot, model.StoredFrame(cjson.decode(raw))) + self._slots[slot] = new + + def _send(self, slot_num): + slot = self._slots[slot_num] + slot.seqno += 1 + slot.owner = self.me + + self._notify(slot_num, slot.seqno, slot.owner, + cjson.encode(model.keys[slot_num].collect())) + + logger.debug('_send slot=%s seqno=%d' % (slot_num, slot.seqno)) + + def _frame_changed_cb(self, sender, slot): + self._send(slot) diff --git a/model.py b/model.py new file mode 100644 index 0000000..6d34dfb --- /dev/null +++ b/model.py @@ -0,0 +1,226 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import cjson +import math + +from theme import * + +keys = [] + +class KeyFrame: + def empty(self): + return self.joints == None + + def _setjoints(self, joints, sticks, middle): + if self.empty(): + return + + # have to traverse in order because + # parent joints must be set right + for stickname in STICKLIST: + (angle,len) = sticks[stickname] + jname = JOINTS[stickname] + (x,y) = getparentjoint(jname, joints, middle) + parents = getparentsticks(stickname) + panglesum = 0 + for parentname in parents: + (pangle,plen) = sticks[parentname] + panglesum += pangle + (nx,ny) = self._getpoints(x,y,angle+panglesum,len) + joints[jname] = (nx,ny) + + def _getpoints(self, x, y, angle, len): + nx = int(round(x + (len * math.cos(math.radians(angle))))) + ny = int(round(y - (len * math.sin(math.radians(angle))))) + return (nx,ny) + + def _initjoints(self): + joints = {} + for stickname in JOINTS: + jname = JOINTS[stickname] + joints[jname] = (0,0) + return joints + +class StoredFrame(KeyFrame): + def __init__(self, data=None): + if not data: + self.clear() + else: + def array2tuple(a): + return a and (a[0], a[1]) + + def hash2tuple(h): + if not h: + return None + out = {} + for i, j in h.items(): + out[i] = array2tuple(j) + return out + + self.x = scale_keyframe(data['x']) + self.middle = scale_middle(data['middle']) + self.parts = data['parts'] + self.sticks = hash2tuple(data['sticks']) + self.joints = hash2tuple(data['joints']) + self._make_thumbs() + self.setjoints() + + def collect(self): + return { 'x' : unscale_keyframe(self.x), + 'middle' : unscale_middle(self.middle), + 'parts' : self.parts, + 'sticks' : self.sticks, + 'joints' : self.joints } + + def setjoints(self): + if not self.empty(): + self._setjoints(self.joints, self.sticks, self.middle) + + def clear(self): + self.middle = None + self.parts = None + self.sticks = None + self.scaled_sticks = None + self.joints = None + self.scaled_joints = None + + def move(self, dx): + if self.scaled_joints: + for jname in self.scaled_joints: + (jx, jy) = self.scaled_joints[jname] + self.scaled_joints[jname] = (jx+dx, jy) + self.x += dx + + def assign(self, x): + self.middle = x.middle + self.parts = x.parts.copy() + self.sticks = x.sticks.copy() + self.joints = x.joints.copy() + self._make_thumbs() + + def _make_thumbs(self): + if self.empty(): + self.scaled_sticks = None + self.scaled_joints = None + return + + self.scaled_sticks = self.sticks.copy() + self.scaled_joints = self._initjoints() + + for key in self.scaled_sticks: + (angle,len) = self.scaled_sticks[key] + newlen = int(len * .2) + self.scaled_sticks[key] = (angle,newlen) + + self._setjoints(self.scaled_joints, self.scaled_sticks, + (self.x, KEYFRAMEHEIGHT/2)) + +def save(filename): + out = [] + + for i in keys: + out.append(i.collect()) + + file(filename, 'w').write(cjson.encode(out)) + +def load(filename): + inc = cjson.decode(file(filename, 'r').read()) + + for i, data in enumerate(inc): + keys[i] = StoredFrame(data) + +def getparentsticks(stickname): + if stickname in ['RIGHT SHOULDER','LEFT SHOULDER','NECK','TORSO']: + return [] + if stickname in ['HEAD']: + return ['NECK'] + if stickname == 'UPPER RIGHT ARM': + return ['RIGHT SHOULDER'] + if stickname == 'LOWER RIGHT ARM': + return ['UPPER RIGHT ARM','RIGHT SHOULDER'] + if stickname == 'UPPER LEFT ARM': + return ['LEFT SHOULDER'] + if stickname == 'LOWER LEFT ARM': + return ['UPPER LEFT ARM','LEFT SHOULDER'] + if stickname == 'RIGHT HIP': + return ['TORSO'] + if stickname == 'UPPER RIGHT LEG': + return ['RIGHT HIP','TORSO'] + if stickname == 'LOWER RIGHT LEG': + return ['UPPER RIGHT LEG','RIGHT HIP','TORSO'] + if stickname == 'RIGHT FOOT': + return ['LOWER RIGHT LEG','UPPER RIGHT LEG','RIGHT HIP','TORSO'] + if stickname == 'LEFT HIP': + return ['TORSO'] + if stickname == 'UPPER LEFT LEG': + return ['LEFT HIP','TORSO'] + if stickname == 'LOWER LEFT LEG': + return ['UPPER LEFT LEG','LEFT HIP','TORSO'] + if stickname == 'LEFT FOOT': + return ['LOWER LEFT LEG','UPPER LEFT LEG','LEFT HIP','TORSO'] + +def getparentjoint(jname, joints, middle): + if jname in ['rightshoulder','leftshoulder','groin','neck']: + return middle + + parentjoints = {'rightelbow':'rightshoulder', + 'righthand':'rightelbow', + 'leftelbow':'leftshoulder', + 'lefthand':'leftelbow', + 'righthip':'groin', + 'rightknee':'righthip', + 'rightheel':'rightknee', + 'righttoe':'rightheel', + 'lefthip':'groin', + 'leftknee':'lefthip', + 'leftheel':'leftknee', + 'lefttoe':'leftheel', + 'head':'neck'} + + return joints[parentjoints[jname]] + +def screen_shot(pixbuf): + tmpdir = '/tmp' + + filename = 'fp%03d.png' % i + filepath = os.path.join(tmpdir,filename) + pixbuf.save(filepath,'png') + + from sugar.datastore import datastore + mediaObject = datastore.create() + mediaObject.metadata['title'] = 'FlipSticks PNG' + thumbData = _get_base64_pixbuf_data(pixbuf) + mediaObject.metadata['preview'] = thumbData + #medaiObject.metadata['icon-color'] = '' + mediaObject.metadata['mime_type'] = 'image/png' + mediaObject.file_path = filepath + datastore.write(mediaObject) + +def _save_data_to_buffer_cb(buf, data): + data[0] += buf + return True + +def _get_base64_pixbuf_data(pixbuf): + data = [""] + pixbuf.save_to_callback(_save_data_to_buffer_cb, "png", {}, data) + import base64 + return base64.b64encode(str(data[0])) + +for i in range(5): + key = StoredFrame() + keyframe_width = KEYFRAMEWIDTH/5 + key.x = keyframe_width/2 + i*keyframe_width + keys.append(key) diff --git a/montage.py b/montage.py new file mode 100644 index 0000000..db02a97 --- /dev/null +++ b/montage.py @@ -0,0 +1,824 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +### flipsticks +### +### author: Ed Stoner (ed@whsd.net) +### (c) 2007 World Wide Workshop Foundation + +import os +import gtk +import math +import gobject +import logging +from gobject import SIGNAL_RUN_FIRST, TYPE_PYOBJECT +from gettext import gettext as _ + +from sugar.activity.activity import get_bundle_path + +import model +import screen +import kinematic +import theme +from theme import * + +logger = logging.getLogger('flipsticks') + +class View(gtk.EventBox): + __gsignals__ = { + 'frame-changed' : (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]) } + + def set_keyframe(self, value): + i, key = value + logger.debug('set_keyframe[%d]=%s' % (i, key and key.collect())) + if not key: + model.keys[i].clear() + else: + model.keys[i] = key + self.restore() + + keyframe = gobject.property(type=object, getter=None, setter=set_keyframe) + + def reset(self): + self.key.reset() + self.selectstickebox() + self.drawmainframe() + + def setframe(self): + model.keys[self.kfselected].assign(self.key) + self.drawkeyframe() + self.emit('frame-changed', self.kfselected) + + def clearframe(self): + model.keys[self.kfselected].clear() + self.drawkeyframe() + self.emit('frame-changed', self.kfselected) + + def setplayspeed(self, value): + self.waittime = int((100-value)*5) + if self.playing: + gobject.source_remove(self.playing) + self.playing = gobject.timeout_add(self.waittime, self.playframe) + + def playbackwards(self): + if self.playing: + gobject.source_remove(self.playing) + + self.frames = kinematic.makeframes() + fsecs = self.frames.keys() + fsecs.sort() + if fsecs: + self.playframenum = fsecs[-1] + else: + self.playframenum = -1 + self.playingbackwards = True + + logger.debug('playbackwards speed=%s' % self.waittime) + self.playing = gobject.timeout_add(self.waittime, self.playframe) + + def playforwards(self): + if self.playing: + gobject.source_remove(self.playing) + + self.frames = kinematic.makeframes() + fsecs = self.frames.keys() + fsecs.sort() + if fsecs: + self.playframenum = fsecs[0] + else: + self.playframenum = -1 + + logger.debug('playforwards speed=%s' % self.waittime) + self.playingbackwards = False + self.playing = gobject.timeout_add(self.waittime, self.playframe) + + def stop(self): + if not self.playing: + return + + gobject.source_remove(self.playing) + self.playing = None + + # set the main window to the keyframe + if not model.keys[self.kfselected].empty(): + self.key.assign(model.keys[self.kfselected]) + self.drawmainframe() + self.updateentrybox() + + def exportframe(self): + self.frames = kinematic.makeframes() + fsecs = self.frames.keys() + firstpixindex = fsecs[0] + + x, y, width, height = self.mfdraw.get_allocation() + pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) + self._draw_frame(fsecs[0], pixmap) + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) + gtk.gdk.Pixbuf.get_from_drawable(pixbuf,pixmap,pixmap.get_colormap(),0,0,0,0,width,height) + + model.screen_shot(pixbuf) + + def restore(self): + self.drawkeyframe() + self.syncmaintokf() + self.updateentrybox() + + def expose_event(self, widget, event): + x , y, width, height = event.area + widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], + self.pixmap, x, y, x, y, width, height) + return False + + def kf_expose_event(self, widget, event): + x , y, width, height = event.area + widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], + self.kfpixmap, x, y, x, y, width, height) + return False + + def configure_event(self, widget, event): + x, y, width, height = self.mfdraw.get_allocation() + self.pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) + self.drawmainframe() + return True + + def kf_configure_event(self, widget, event): + self.drawkeyframe() + return True + + def motion_notify_event(self, widget, event): + if event.is_hint: + x, y, state = event.window.get_pointer() + else: + x = event.x + y = event.y + state = event.state + if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: + if self.jointpressed: + if _inarea(widget, x, y): + #self.key.joints[self.jointpressed] = (x,y) # old hack way + # first find the parents x,y + (px,py) = model.getparentjoint(self.jointpressed,self.key.joints, + self.key.middle) + if x-px == 0: + #computeangle = 0 + b = 1 + else: + b = float(px-x) + a = float(y-py) + computeangle = int(math.degrees(math.atan(a/b))) + stickname = JOINTTOSTICK[self.jointpressed] + # add sum of parent angles to new angle + parents = model.getparentsticks(stickname) + panglesum = 0 + for parentname in parents: + (pangle,plen) = self.key.sticks[parentname] + panglesum += pangle + (angle, len) = self.key.sticks[stickname] + #print 'X:%s,Y:%s,PX:%s,PY:%s,ANGLE:%s,NEWANGLE:%s' % (x,y,px,py,angle,newangle) + newangle = computeangle-panglesum + if (x < px) or (b == 1): + newangle = newangle + 180 + if newangle < 0: + newangle = 360 + newangle + self.key.sticks[stickname] = (newangle,len) + self.key.setjoints() # this is overkill + self.drawmainframe() + self.updateentrybox() + elif self.middlepressed: + if _inarea(widget, x, y): + xdiff = x-self.key.middle[0] + ydiff = y-self.key.middle[1] + self.key.move(xdiff, ydiff) + self.key.middle = (x,y) + self.drawmainframe() + elif self.rotatepressed: + if _inarea(widget, x, y): + (px,py) = self.key.middle + if x-px == 0: + #computeangle = 0 + b = 1 + else: + b = float(px-x) + a = float(y-py) + computeangle = int(math.degrees(math.atan(a/b))) + stickname = 'TORSO' + (angle, len) = self.key.sticks[stickname] + newangle = computeangle + if (x < px) or (b == 1): + newangle = newangle + 180 + if newangle < 0: + newangle = 360 + newangle + anglediff = newangle-angle + self.key.sticks[stickname] = (newangle,len) + # now rotate the other sticks off of the middle + for stickname in ['NECK','RIGHT SHOULDER','LEFT SHOULDER']: + (sangle,slen) = self.key.sticks[stickname] + newsangle = sangle+anglediff + if newsangle < 0: + newsangle = 360 + newsangle + if newsangle > 360: + newsangle = newsangle - 360 + self.key.sticks[stickname] = (newsangle,slen) + self.key.setjoints() + self.drawmainframe() + self.updateentrybox() + + return True + + def kf_motion_notify_event(self, widget, event): + if event.is_hint: + x, y, state = event.window.get_pointer() + else: + x = event.x + y = event.y + state = event.state + if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: + if self.kfpressed >= 0: + if _inarea(widget, x, y): + xdiff = int(x - self.kf_mouse_pos) + frame = model.keys[self.kfpressed] + if frame.x + xdiff > KEYFRAME_RADIUS \ + and frame.x + xdiff < KEYFRAMEWIDTH-KEYFRAME_RADIUS: + frame.move(xdiff) + + if self._emit_move_handle: + gobject.source_remove(self._emit_move_handle) + if self._emit_move_key != self.kfpressed: + self._emit_move(self._emit_move_key) + + self._emit_move_key = self.kfpressed + self._emit_move_handle = gobject.timeout_add( + MOVEMIT_TIMEOUT, self._emit_move, + self.kfpressed) + + self.drawkeyframe() + self.kf_mouse_pos = x + return True + + def button_press_event(self, widget, event): + if event.button == 1 and self.pixmap != None: + joint = self.key.injoint(event.x, event.y) + if joint: + self.jointpressed = joint + self.drawmainframe() + elif self.key.inmiddle(event.x, event.y): + self.middlepressed = True + self.drawmainframe() + elif self.key.inrotate(event.x, event.y): + self.rotatepressed = True + self.drawmainframe() + return True + + def syncmaintokf(self): + # set the main window to the keyframe + if not model.keys[self.kfselected].empty(): + self.key.assign(model.keys[self.kfselected]) + self.drawmainframe() + + def kf_button_press_event(self, widget, event): + if event.button == 1 and self.pixmap != None: + kfnum = self._inkeyframe(event.x, event.y) + if kfnum >= 0: + self.kf_mouse_pos = event.x + self.kfpressed = kfnum + self.kfselected = kfnum + self.drawkeyframe() + self.syncmaintokf() + self.updateentrybox() + return True + + def button_release_event(self, widget, event): + self.jointpressed = None + self.middlepressed = False + self.rotatepressed = False + self.drawmainframe() + return True + + def kf_button_release_event(self, widget, event): + self.kfpressed = -1 + self.drawkeyframe() + return True + + def playframe(self): + if not self.playing: + return False + else: + if self.playframenum == -1: + return True + + self._draw_frame(self.playframenum, self.pixmap) + # draw circle for middle + #green = cm.alloc_color('green') + #drawgc.set_foreground(green) + #x,y = middle + #self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + self.mfdraw.queue_draw() + + fsecs = self.frames.keys() + fsecs.sort() + if self.playingbackwards: + # decrement playframenum + if self.playframenum == fsecs[0]: + self.playframenum = fsecs[-1] + else: + i = fsecs.index(self.playframenum) + self.playframenum = fsecs[i-1] + else: + # increment playframenum + if self.playframenum == fsecs[-1]: + self.playframenum = fsecs[0] + else: + i = fsecs.index(self.playframenum) + self.playframenum = fsecs[i+1] + if self.playing: + return True + else: + return False + + def enterangle_callback(self, widget, entry): + stickname = self.stickselected + if stickname in self.key.sticks: + newangle = int(entry.get_text()) + (angle, len) = self.key.sticks[stickname] + self.key.sticks[stickname] = (newangle,len) + self.anglel_adj.set_value(newangle) + self.anglel_slider.set_sensitive(True) + else: + # part not stick + self.angleentry.set_text('') + self.angleentry.set_sensitive(False) + self.anglel_adj.set_value(0) + self.anglel_slider.set_sensitive(False) + + self.key.setjoints() + self.drawmainframe() + + def updateentrybox(self): + if self.stickselected in self.key.sticks: + (angle, len) = self.key.sticks[self.stickselected] + self.angleentry.set_text(str(angle)) + self.anglel_adj.set_value(angle) + else: + # part not stick + len = self.key.parts[self.stickselected] + self.sizeentry.set_text(str(len)) + self.size_adj.set_value(len) + + def enterlen_callback(self, widget, entry): + stickname = self.stickselected + newlen = int(entry.get_text()) + if stickname in self.key.sticks: + if stickname == 'HEAD': + newlen = int(newlen/2.0) + (angle, len) = self.key.sticks[stickname] + self.key.sticks[stickname] = (angle,newlen) + else: + # part not stick + self.key.parts[stickname] = newlen + self.key.setjoints() + self.drawmainframe() + + def drawmainframe(self): + if not self.pixmap: + return + + area = self.toplevel.window + drawgc = area.new_gc() + drawgc.line_width = 3 + cm = drawgc.get_colormap() + red = cm.alloc_color('red') + yellow = cm.alloc_color('yellow') + white = cm.alloc_color('white') + black = cm.alloc_color('black') + blue = cm.alloc_color('blue') + green = cm.alloc_color('green') + drawgc.fill = gtk.gdk.SOLID + x, y, width, height = self.mfdraw.get_allocation() + #self.pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) + # clear area + drawgc.set_foreground(white) + self.pixmap.draw_rectangle(drawgc,True,0,0,width,height) + + drawgc.set_foreground(black) + hsize = self.key.sticks['HEAD'][1] # really half of head size + rhsize = self.key.parts['RIGHT HAND'] + lhsize = self.key.parts['LEFT HAND'] + self.drawstickman(drawgc,self.pixmap,self.key.middle,self.key.joints,hsize,rhsize,lhsize) + # draw circle for middle + drawgc.set_foreground(green) + if self.middlepressed: + drawgc.set_foreground(blue) + x,y = self.key.middle + self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + # draw circle for rotate (should be halfway between middle and groin + (rx,ry) = self.key.getrotatepoint() + drawgc.set_foreground(yellow) + if self.rotatepressed: + drawgc.set_foreground(blue) + self.pixmap.draw_arc(drawgc,True,rx-5,ry-5,10,10,0,360*64) + # draw circles for joints + drawgc.set_foreground(black) + for jname in self.key.joints: + if jname == 'head': + continue + x,y = self.key.joints[jname] + if self.jointpressed == jname: + drawgc.set_foreground(blue) + self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + drawgc.set_foreground(black) + else: + drawgc.set_foreground(red) + self.pixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + drawgc.set_foreground(black) + self.mfdraw.queue_draw() + + def drawstickman(self,drawgc,pixmap,middle,joints,hsize,rhsize,lhsize): + leftarm = [middle, joints['leftshoulder'],joints['leftelbow'],joints['lefthand']] + rightarm = [middle, joints['rightshoulder'],joints['rightelbow'],joints['righthand']] + torso = [joints['neck'],middle,joints['groin']] + leftleg = [joints['groin'],joints['lefthip'],joints['leftknee'], + joints['leftheel'],joints['lefttoe']] + rightleg = [joints['groin'],joints['righthip'],joints['rightknee'], + joints['rightheel'],joints['righttoe']] + # draw lines + pixmap.draw_lines(drawgc, leftarm) + pixmap.draw_lines(drawgc, rightarm) + pixmap.draw_lines(drawgc, torso) + pixmap.draw_lines(drawgc, leftleg) + pixmap.draw_lines(drawgc, rightleg) + # draw head + x,y = joints['head'] + pixmap.draw_arc(drawgc,True,x-hsize,y-hsize,hsize*2,hsize*2,0,360*64) + # draw circles for hands + x,y = joints['righthand'] + pixmap.draw_arc(drawgc,True,x-int(rhsize/2.0),y-int(rhsize/2.0),rhsize,rhsize,0,360*64) + x,y = joints['lefthand'] + pixmap.draw_arc(drawgc,True,x-int(lhsize/2.0),y-int(lhsize/2.0),lhsize,lhsize,0,360*64) + + def drawkeyframe(self): + area = self.toplevel.window + drawgc = area.new_gc() + drawgc.line_width = 2 + cm = drawgc.get_colormap() + red = cm.alloc_color('red') + white = cm.alloc_color('white') + black = cm.alloc_color('black') + blue = cm.alloc_color('blue') + green = cm.alloc_color('green') + pink = cm.alloc_color(PINK) + bgcolor = cm.alloc_color(BACKGROUND) + darkgreen = cm.alloc_color(BUTTON_BACKGROUND) + drawgc.fill = gtk.gdk.SOLID + x, y, width, height = self.kfdraw.get_allocation() + self.kfpixmap = gtk.gdk.Pixmap(self.kfdraw.window, width, height) + # clear area + drawgc.set_foreground(bgcolor) + self.kfpixmap.draw_rectangle(drawgc,True,0,0,width,height) + # draw line in middle + drawgc.set_foreground(darkgreen) + self.kfpixmap.draw_rectangle(drawgc,True,10,int(height/2.0)-5,width-20,10) + x = 10 + y = int(height/2.0) + self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + x = width-10 + self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + + # draw the keyframe circles + for i in list(reversed(self.keys_overlap_stack)): + # first the outer circle + x = model.keys[i].x + if i == self.kfselected: + drawgc.set_foreground(pink) + else: + drawgc.set_foreground(darkgreen) + self.kfpixmap.draw_arc(drawgc, True, x-KEYFRAME_RADIUS, + y-KEYFRAME_RADIUS, KEYFRAME_RADIUS*2, KEYFRAME_RADIUS*2, + 0, 360*64) + # then the inner circle + drawgc.set_foreground(white) + self.kfpixmap.draw_arc(drawgc, True, x-KEYFRAME_RADIUS+5, + y-KEYFRAME_RADIUS+5, (KEYFRAME_RADIUS-5)*2, + (KEYFRAME_RADIUS-5)*2, 0, 360*64) + if model.keys[i].scaled_sticks: + # draw a man in the circle + drawgc.set_foreground(black) + hsize = model.keys[i].scaled_sticks['HEAD'][1] + rhsize = int(model.keys[i].parts['RIGHT HAND']*0.2) + lhsize = int(model.keys[i].parts['LEFT HAND']*0.2) + self.drawstickman(drawgc,self.kfpixmap,(x,y), model.keys[i].scaled_joints,hsize,rhsize,lhsize) + #self.kfpixmap.draw_arc(drawgc,True,x-5,y-5,10,10,0,360*64) + self.kfdraw.queue_draw() + + def drawfp(self): + area = self.toplevel.window + drawgc = area.new_gc() + drawgc.line_width = 1 + cm = drawgc.get_colormap() + red = cm.alloc_color('red') + white = cm.alloc_color('white') + black = cm.alloc_color('black') + blue = cm.alloc_color('blue') + green = cm.alloc_color('green') + pink = cm.alloc_color(PINK) + bgcolor = cm.alloc_color(BACKGROUND) + darkgreen = cm.alloc_color(BUTTON_BACKGROUND) + drawgc.fill = gtk.gdk.SOLID + x, y, width, height = self.fpdraw.get_allocation() + self.fppixmap = gtk.gdk.Pixmap(self.fpdraw.window, width, height) + # clear area + drawgc.set_foreground(white) + self.fppixmap.draw_rectangle(drawgc,True,0,0,width,height) + self.fpdraw.queue_draw() + + def selectstick(self, widget, event, data=None): + if data: + if self.stickselected: + ebox = self.stickbuttons[self.stickselected] + ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) + label = ebox.get_child() + label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_FOREGROUND)) + self.stickselected = data + self.selectstickebox() + + def selectstickebox(self): + ebox = self.stickbuttons[self.stickselected] + ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_FOREGROUND)) + label = ebox.get_child() + label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) + + if self.stickselected in self.key.sticks: + if self.stickselected == 'HEAD': + size = self.key.sticks[self.stickselected][1]*2 + else: + size = self.key.sticks[self.stickselected][1] + + angle = self.key.sticks[self.stickselected][0] + self.angleentry.set_text(str(angle)) + self.angleentry.set_sensitive(True) + self.anglel_adj.set_value(angle) + self.anglel_slider.set_sensitive(True) + else: + size = self.key.parts[self.stickselected] + + # its a part not a stick + self.angleentry.set_text('') + self.angleentry.set_sensitive(False) + self.anglel_adj.set_value(0) + self.anglel_slider.set_sensitive(False) + + self.sizeentry.set_text(str(size)) + self.size_adj.set_value(size) + + def __init__(self, activity): + gtk.EventBox.__init__(self) + + self.playing = False + self.playingbackwards = False + self.toplevel = activity + self.stickselected = 'RIGHT SHOULDER' + self.pixmap = None + self._emit_move_handle = None + + self.setplayspeed(50) + + self.keys_overlap_stack = [] + for i in range(len(model.keys)): + self.keys_overlap_stack.append(i) + + self.key = screen.ScreenFrame() + + self.kfselected = 0 + self.jointpressed = None + self.kfpressed = -1 + self.middlepressed = False + self.rotatepressed = False + self.iconsdir = os.path.join(get_bundle_path(), 'icons') + self.language = 'English' + + # screen + + self.mfdraw = gtk.DrawingArea() + self.mfdraw.connect('expose_event', self.expose_event) + self.mfdraw.connect('configure_event', self.configure_event) + self.mfdraw.connect('motion_notify_event', self.motion_notify_event) + self.mfdraw.connect('button_press_event', self.button_press_event) + self.mfdraw.connect('button_release_event', self.button_release_event) + self.mfdraw.set_events(gtk.gdk.EXPOSURE_MASK + | gtk.gdk.LEAVE_NOTIFY_MASK + | gtk.gdk.BUTTON_PRESS_MASK + | gtk.gdk.BUTTON_RELEASE_MASK + | gtk.gdk.POINTER_MOTION_MASK + | gtk.gdk.POINTER_MOTION_HINT_MASK) + self.mfdraw.set_size_request(DRAWWIDTH, DRAWHEIGHT) + + screen_box = gtk.EventBox() + screen_box.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) + screen_box.set_border_width(PAD/2) + screen_box.add(self.mfdraw) + + screen_pink = gtk.EventBox() + screen_pink.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) + screen_pink.set_border_width(PAD) + screen_pink.add(screen_box) + + # keyframes + + self.kfdraw = gtk.DrawingArea() + self.kfdraw.set_size_request(KEYFRAMEWIDTH,KEYFRAMEHEIGHT) + self.kfdraw.connect('expose_event', self.kf_expose_event) + self.kfdraw.connect('configure_event', self.kf_configure_event) + self.kfdraw.connect('motion_notify_event', self.kf_motion_notify_event) + self.kfdraw.connect('button_press_event', self.kf_button_press_event) + self.kfdraw.connect('button_release_event', self.kf_button_release_event) + self.kfdraw.set_events(gtk.gdk.EXPOSURE_MASK + | gtk.gdk.LEAVE_NOTIFY_MASK + | gtk.gdk.BUTTON_PRESS_MASK + | gtk.gdk.BUTTON_RELEASE_MASK + | gtk.gdk.POINTER_MOTION_MASK + | gtk.gdk.POINTER_MOTION_HINT_MASK) + + kfdraw_box = gtk.EventBox() + kfdraw_box.set_border_width(PAD) + kfdraw_box.add(self.kfdraw) + + # control box + + angle_box = gtk.HBox() + anglelabel = gtk.Label(_('Angle:')) + anglelabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) + anglelabel.set_size_request(60, -1) + angle_box.pack_start(anglelabel,False,False,5) + self.angleentry = gtk.Entry() + self.angleentry.set_max_length(3) + self.angleentry.set_width_chars(3) + self.angleentry.connect('activate', self.enterangle_callback, self.angleentry) + self.angleentry.modify_bg(gtk.STATE_INSENSITIVE, + gtk.gdk.color_parse(BUTTON_FOREGROUND)) + angle_box.pack_start(self.angleentry,False,False,0) + self.anglel_adj = gtk.Adjustment(0, 0, 360, 1, 60, 0) + self.anglel_adj.connect('value_changed', self._anglel_adj_cb) + self.anglel_slider = gtk.HScale(self.anglel_adj) + self.anglel_slider.set_draw_value(False) + for state, color in COLOR_BG_BUTTONS: + self.anglel_slider.modify_bg(state, gtk.gdk.color_parse(color)) + angle_box.pack_start(self.anglel_slider) + + size_box = gtk.HBox() + sizelabel = gtk.Label(_('Size:')) + sizelabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_BACKGROUND)) + sizelabel.set_size_request(60, -1) + size_box.pack_start(sizelabel,False,False,5) + self.sizeentry = gtk.Entry() + self.sizeentry.set_max_length(3) + self.sizeentry.set_width_chars(3) + self.sizeentry.connect('activate', self.enterlen_callback, self.sizeentry) + self.sizeentry.modify_bg(gtk.STATE_INSENSITIVE, + gtk.gdk.color_parse(BUTTON_FOREGROUND)) + size_box.pack_start(self.sizeentry,False,False,0) + self.size_adj = gtk.Adjustment(0, 0, 200, 1, 30, 0) + self.size_adj.connect('value_changed', self._size_adj_cb) + size_slider = gtk.HScale(self.size_adj) + size_slider.set_draw_value(False) + for state, color in COLOR_BG_BUTTONS: + size_slider.modify_bg(state, gtk.gdk.color_parse(color)) + size_box.pack_start(size_slider) + + control_head_box = gtk.VBox() + control_head_box.pack_start(angle_box) + control_head_box.pack_start(size_box) + + control_head = gtk.EventBox() + control_head.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_FOREGROUND)) + control_head.add(control_head_box) + + control_options = gtk.VBox() + self.stickbuttons = {} + self.sticklabels = {} + for stickpartname in LABELLIST: + label = gtk.Label(LANG[self.language][stickpartname]) + label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(BUTTON_FOREGROUND)) + self.sticklabels[stickpartname] = label + ebox = gtk.EventBox() + ebox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) + ebox.set_events(gtk.gdk.BUTTON_PRESS_MASK) + ebox.connect('button_press_event',self.selectstick,stickpartname) + ebox.add(label) + self.stickbuttons[stickpartname] = ebox + control_options.pack_start(ebox,False,False,0) + self.selectstickebox() + + control_scroll = gtk.ScrolledWindow() + control_scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + control_scroll.add_with_viewport(control_options) + control_options.get_parent().modify_bg(gtk.STATE_NORMAL, + gtk.gdk.color_parse(BUTTON_BACKGROUND)) + + control_box = gtk.VBox() + control_box.pack_start(control_head, False, False, 0) + control_box.pack_start(control_scroll) + + control_bg = gtk.EventBox() + control_bg.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BUTTON_BACKGROUND)) + control_bg.set_border_width(PAD/2) + control_bg.add(control_box) + + control_pink = gtk.EventBox() + control_pink.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(PINK)) + control_pink.set_border_width(PAD) + control_pink.add(control_bg) + + # left control box + + logo = gtk.Image() + logo.set_from_file(os.path.join(self.iconsdir,'logo.png')) + + leftbox = gtk.VBox() + leftbox.pack_start(logo, False, False) + leftbox.pack_start(control_pink) + + # desktop + + hdesktop = gtk.HBox() + hdesktop.pack_start(leftbox, False) + hdesktop.pack_start(screen_pink, False, False) + + desktop = gtk.VBox() + desktop.pack_start(hdesktop) + desktop.pack_start(kfdraw_box, False, False, 0) + + greenbox = gtk.EventBox() + greenbox.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(BACKGROUND)) + greenbox.set_border_width(PAD/2) + greenbox.add(desktop) + + self.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(YELLOW)) + self.add(greenbox) + self.show_all() + + def _draw_frame(self, index, pixmap): + joints = self.frames[index].joints + parts = self.frames[index].parts + # draw on the main drawing area + area = self.toplevel.window + drawgc = area.new_gc() + drawgc.line_width = 3 + cm = drawgc.get_colormap() + white = cm.alloc_color('white') + black = cm.alloc_color('black') + drawgc.fill = gtk.gdk.SOLID + x, y, width, height = self.mfdraw.get_allocation() + #pixmap = gtk.gdk.Pixmap(self.mfdraw.window, width, height) + # clear area + drawgc.set_foreground(white) + pixmap.draw_rectangle(drawgc,True,0,0,width,height) + + drawgc.set_foreground(black) + #hsize = self.key.sticks['HEAD'][1] # really half of head size + hsize = self.frames[index].hsize + middle = self.frames[index].middle + rhsize = parts['RIGHT HAND'] + lhsize = parts['LEFT HAND'] + self.drawstickman(drawgc, pixmap, middle, joints, hsize, rhsize, lhsize) + + def _inkeyframe(self, x, y): + dy = math.pow(abs(y - KEYFRAMEHEIGHT/2), 2) + + for i, key in enumerate(self.keys_overlap_stack): + dx = math.pow(abs(x - model.keys[key].x), 2) + l = math.sqrt(dx + dy) + if int(l) <= KEYFRAME_RADIUS: + self.keys_overlap_stack.pop(i) + self.keys_overlap_stack.insert(0, key) + return key + + return -1 + + def _anglel_adj_cb(self, adj): + self.angleentry.set_text(str(int(adj.value))) + self.enterangle_callback(None, self.angleentry) + + def _size_adj_cb(self, adj): + self.sizeentry.set_text(str(int(adj.value))) + self.enterlen_callback(None, self.sizeentry) + + def _emit_move(self, key): + self._emit_move_handle = None + self.emit('frame-changed', key) + return False + +def _inarea(widget, x, y): + x_, y_, width, height = widget.get_allocation() + if x < 0 or y < 0 or x >= width or y >= height: + return False + return True diff --git a/notes.txt b/notes.txt deleted file mode 100644 index 40f2941..0000000 --- a/notes.txt +++ /dev/null @@ -1,7 +0,0 @@ -linear interpolation: -this is what to do to get intermediate points - -skeletal animation: -what the program is doing - - diff --git a/png2theora b/png2theora deleted file mode 100755 index 60efde1..0000000 --- a/png2theora +++ /dev/null @@ -1,117 +0,0 @@ -#! /bin/sh - -# png2theora - temporary wrapper script for .libs/png2theora -# Generated by ltmain.sh - GNU libtool 1.5.22 Debian 1.5.22-4 (1.1220.2.365 2005/12/18 22:14:06) -# -# The png2theora program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='/usr/bin/sed -e 1s/^X//' -sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g' - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command="(cd /tmp/libtheora-1.0beta2/examples; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=\"/usr/local/bin:/usr/bin:/bin:/opt/bin:/usr/i686-pc-linux-gnu/gcc-bin/3.4.4:/opt/blackdown-jre-1.4.2.03/bin:/usr/qt/3/bin:/opt/panda3d/bin\"; export PATH; gcc -I/usr/include/libpng12 -Wall -Wno-parentheses -O3 -fforce-addr -fomit-frame-pointer -finline-functions -funroll-loops -o \$progdir/\$file png2theora-png2theora.o ../lib/.libs/libtheora.so /usr/lib/libogg.so /usr/lib/libpng12.so -Wl,--rpath -Wl,/tmp/libtheora-1.0beta2/lib/.libs)" - -# This environment variable determines our operation mode. -if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then - # install mode needs the following variable: - notinst_deplibs=' ../lib/libtheora.la' -else - # When we are sourced in execute mode, $file and $echo are already set. - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - echo="echo" - file="$0" - # Make sure echo works. - if test "X$1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift - elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then - # Yippee, $echo works! - : - else - # Restart under the correct shell, and then maybe $echo will work. - exec /bin/sh "$0" --no-reexec ${1+"$@"} - fi - fi - - # Find the directory that this script lives in. - thisdir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "x$thisdir" = "x$file" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'` - while test -n "$file"; do - destdir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - - # If there was a directory component, then change thisdir. - if test "x$destdir" != "x$file"; then - case "$destdir" in - [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; - *) thisdir="$thisdir/$destdir" ;; - esac - fi - - file=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'` - done - - # Try to get the absolute directory name. - absdir=`cd "$thisdir" && pwd` - test -n "$absdir" && thisdir="$absdir" - - program=lt-'png2theora' - progdir="$thisdir/.libs" - - if test ! -f "$progdir/$program" || \ - { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /usr/bin/sed 1q`; \ - test "X$file" != "X$progdir/$program"; }; then - - file="$$-$program" - - if test ! -d "$progdir"; then - mkdir "$progdir" - else - rm -f "$progdir/$file" - fi - - # relink executable if necessary - if test -n "$relink_command"; then - if relink_command_output=`eval $relink_command 2>&1`; then : - else - echo "$relink_command_output" >&2 - rm -f "$progdir/$file" - exit 1 - fi - fi - - mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || - { rm -f "$progdir/$program"; - mv -f "$progdir/$file" "$progdir/$program"; } - rm -f "$progdir/$file" - fi - - if test -f "$progdir/$program"; then - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - # Run the actual program with our arguments. - - exec "$progdir/$program" ${1+"$@"} - - $echo "$0: cannot exec $program ${1+"$@"}" - exit 1 - fi - else - # The program doesn't exist. - $echo "$0: error: \`$progdir/$program' does not exist" 1>&2 - $echo "This script is just a wrapper for $program." 1>&2 - echo "See the libtool documentation for more information." 1>&2 - exit 1 - fi -fi diff --git a/screen.py b/screen.py new file mode 100644 index 0000000..bd31199 --- /dev/null +++ b/screen.py @@ -0,0 +1,68 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import theme +import model + +class ScreenFrame(model.KeyFrame): + def __init__(self): + self.reset() + + def setjoints(self): + self._setjoints(self.joints, self.sticks, self.middle) + + def reset(self): + self.parts = theme.PARTS.copy() + self.sticks = theme.STICKS.copy() + self.joints = self._initjoints() + self.middle = (theme.DRAWWIDTH/2, theme.DRAWHEIGHT/3) + self.setjoints() + + def assign(self, x): + self.middle = x.middle + self.parts = x.parts.copy() + self.sticks = x.sticks.copy() + self.joints = x.joints.copy() + self.setjoints() + + def getrotatepoint(self): + (angle,len) = self.sticks['TORSO'] + x,y = self.middle + (rx,ry) = self._getpoints(x,y,angle,int(len/2.0)) + return (rx,ry) + + def inrotate(self, x, y): + rx, ry = self.getrotatepoint() + if (abs(rx-x) <= 5) and (abs(ry-y) <= 5): + return True + return False + + def injoint(self, x, y): + for jname in self.joints: + jx, jy = self.joints[jname] + if (abs(jx-x) <= 5) and (abs(jy-y) <= 5): + return jname + return False + + def inmiddle(self, x, y): + mx, my = self.middle + if (abs(mx-x) <= 5) and (abs(my-y) <= 5): + return True + return False + + def move(self, dx, dy): + if self.joints: + for jname in self.joints: + (jx, jy) = self.joints[jname] + self.joints[jname] = (jx+dx, jy+dy) diff --git a/shared.py b/shared.py new file mode 100644 index 0000000..6725cf0 --- /dev/null +++ b/shared.py @@ -0,0 +1,131 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging +import telepathy +from gobject import property, SIGNAL_RUN_FIRST, TYPE_PYOBJECT + +from sugar.activity.activity import Activity +from sugar.presence.sugartubeconn import SugarTubeConnection + +logger = logging.getLogger('flipsticks') + +class CanvasActivity(Activity): + __gsignals__ = { + 'init' : (SIGNAL_RUN_FIRST, None, []) } + + def __init__(self, canvas, *args): + Activity.__init__(self, *args) + + self._inited = False + + # XXX do it after(possible) read_file() invoking + # have to rely on calling read_file() from map_cb in sugar-toolkit + canvas.connect_after('map', self._map_canvasactivity_cb) + self.set_canvas(canvas) + + def get_inited(self): + return self._inited + + inited = property(type=bool, default=False, getter=get_inited, setter=None) + + def _map_canvasactivity_cb(self, widget): + self._inited = True + self.emit('init') + return False + +class SharedActivity(CanvasActivity): + __gsignals__ = { + 'tube' : (SIGNAL_RUN_FIRST, None, 2*[TYPE_PYOBJECT]) } + + def __init__(self, canvas, service, *args): + CanvasActivity.__init__(self, canvas, *args) + + self.service = service + self._postpone_tubes = [] + + self.connect_after('init', self._init_sharedactivity_cb) + self.connect('shared', self._shared_cb) + + # Owner.props.key + if self._shared_activity: + # We are joining the activity + self.connect('joined', self._joined_cb) + if self.get_shared(): + # We've already joined + self._joined_cb() + + def _init_sharedactivity_cb(self, sender): + for i in self._postpone_tubes: + self.emit('tube', i, self._initiating) + self._postpone_tubes = [] + + def _shared_cb(self, activity): + logger.debug('My activity was shared') + self._initiating = True + self._sharing_setup() + + logger.debug('This is my activity: making a tube...') + id = self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + self.service, {}) + + def _joined_cb(self, activity): + if not self._shared_activity: + return + + logger.debug('Joined an existing shared activity') + + self._initiating = False + self._sharing_setup() + + logger.debug('This is not my activity: waiting for a tube...') + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _sharing_setup(self): + if self._shared_activity is None: + logger.error('Failed to share or join activity') + return + self._conn = self._shared_activity.telepathy_conn + self._tubes_chan = self._shared_activity.telepathy_tubes_chan + self._text_chan = self._shared_activity.telepathy_text_chan + + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + logger.error('ListTubes() failed: %s', e) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and + service == self.service): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + + tube_conn = SugarTubeConnection(self._conn, + self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], + id, group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + if self.get_inited(): + self.emit('tube', tube_conn, self._initiating) + else: + self._postpone_tubes.append(tube_conn) diff --git a/theme.py b/theme.py new file mode 100644 index 0000000..3d3b404 --- /dev/null +++ b/theme.py @@ -0,0 +1,215 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk + +from sugar.graphics import style + +GRAY = "#B7B7B7" # gray +PINK = "#FF0099" # pink +YELLOW = "#FFFF00" # yellow +WHITE = "#FFFFFF" +BLACK = "#000000" +BACKGROUND = "#66CC00" # light green +BUTTON_FOREGROUND = "#CCFB99" # very light green +BUTTON_BACKGROUND = "#027F01" # dark green +COLOR_FG_BUTTONS = ( + (gtk.STATE_NORMAL,"#CCFF99"), + (gtk.STATE_ACTIVE,"#CCFF99"), + (gtk.STATE_PRELIGHT,"#CCFF99"), + (gtk.STATE_SELECTED,"#CCFF99"), + (gtk.STATE_INSENSITIVE,"#CCFF99"), + ) # very light green +COLOR_BG_BUTTONS = ( + (gtk.STATE_NORMAL,"#027F01"), + (gtk.STATE_ACTIVE,"#CCFF99"), + (gtk.STATE_PRELIGHT,"#016D01"), + (gtk.STATE_SELECTED,"#CCFF99"), + (gtk.STATE_INSENSITIVE,"#027F01"), + ) +OLD_COLOR_BG_BUTTONS = ( + (gtk.STATE_NORMAL,"#027F01"), + (gtk.STATE_ACTIVE,"#014D01"), + (gtk.STATE_PRELIGHT,"#016D01"), + (gtk.STATE_SELECTED,"#027F01"), + (gtk.STATE_INSENSITIVE,"#027F01"), + ) + +SPANISH = u'Espa\xf1ol' +LANG = {'English':{'size':'Size', + 'angle':'Angle', + 'lessonplan':'Lesson Plans', + 'lpdir':'lp-en', + 'export':'Export Frame One', + 'HEAD':'Head', + 'NECK':'Neck', + 'RIGHT SHOULDER':'Right Shoulder', + 'UPPER RIGHT ARM':'Upper Right Arm', + 'LOWER RIGHT ARM':'Lower Right Arm', + 'RIGHT HAND':'Right Hand', + 'LEFT SHOULDER':'Left Shoulder', + 'UPPER LEFT ARM':'Upper Left Arm', + 'LOWER LEFT ARM':'Lower Left Arm', + 'LEFT HAND':'Left Hand', + 'TORSO':'Torso', + 'RIGHT HIP':'Right Hip', + 'UPPER RIGHT LEG':'Upper Right Leg', + 'LOWER RIGHT LEG':'Lower Right Leg', + 'RIGHT FOOT':'Right Foot', + 'LEFT HIP':'Left Hip', + 'UPPER LEFT LEG':'Upper Left Leg', + 'LOWER LEFT LEG':'Lower Left Leg', + 'LEFT FOOT':'Left Foot'}, + SPANISH:{'size':u'Tama\xf1o', + 'angle':u'\xe1ngulo', + 'lessonplan':u'Planes de la lecci\xf3n', + 'lpdir':'lp-en', + 'export':'Un marco de la exportacion', + 'HEAD':'Cabeza', + 'NECK':'Cuello', + 'RIGHT SHOULDER':'Hombro derecho', + 'UPPER RIGHT ARM':'Brazo derecho superior', + 'LOWER RIGHT ARM':'Bajar el brazo derecho', + 'RIGHT HAND':'Mano derecha', + 'LEFT SHOULDER':'Hombro izquierdo', + 'UPPER LEFT ARM':'Brazo izquierdo superior', + 'LOWER LEFT ARM':u'Un brazo izquierdo m\xe1s bajo', + 'LEFT HAND':'Mano izquierda', + 'TORSO':'Torso', + 'RIGHT HIP':'Cadera derecha', + 'UPPER RIGHT LEG':'Pierna derecha superior', + 'LOWER RIGHT LEG':'Bajar la pierna derecha', + 'RIGHT FOOT':'Pie derecho', + 'LEFT HIP':'Cadera izquierda', + 'UPPER LEFT LEG':'Pierna izquierda superior', + 'LOWER LEFT LEG':u'Una pierna izquierda m\xe1s baja', + 'LEFT FOOT':'Pie izquierdo'}} + +PAD = 10 +LOGO_WIDTH = 276 +TOLLBAR_HEIGHT = style.LARGE_ICON_SIZE + +KEYFRAMEWIDTH = gtk.gdk.screen_width() - PAD*3 +KEYFRAMEHEIGHT = 80 + +DRAWWIDTH = gtk.gdk.screen_width() - LOGO_WIDTH - PAD*4 +DRAWHEIGHT = gtk.gdk.screen_height() - KEYFRAMEHEIGHT - PAD*6 - TOLLBAR_HEIGHT + +KEYFRAMES = [] +KEYFRAMES_NUMBER = 5 +TOTALFRAMES = 30 + +KEYFRAME_RADIUS = 40 + +MOVEMIT_TIMEOUT = 1000 + +for i in range(KEYFRAMES_NUMBER): + keyframe_width = KEYFRAMEWIDTH/KEYFRAMES_NUMBER + KEYFRAMES.append(keyframe_width/2 + i*keyframe_width) + +# scale coordinates between native resolution and transportable + +TRANSFER_DRAWWIDTH = 350 +TRANSFER_DRAWHEIGHT = 350 +TRANSFER_KEYFRAMEWIDTH = 600 + +def scale_keyframe(x): + factor = float(TRANSFER_KEYFRAMEWIDTH) / (KEYFRAMEWIDTH) + x = max(KEYFRAME_RADIUS, int(x/factor)) + x = min(KEYFRAMEWIDTH-KEYFRAME_RADIUS-1, x) + return x + +def scale_middle(middle): + if not middle: + return None + x_factor = float(TRANSFER_DRAWWIDTH) / DRAWWIDTH + y_factor = float(TRANSFER_DRAWHEIGHT) / DRAWHEIGHT + return (min(DRAWWIDTH-1, int(middle[0]/x_factor)), + min(DRAWHEIGHT-1, int(middle[1]/y_factor))) + +def unscale_keyframe(x): + factor = float(TRANSFER_KEYFRAMEWIDTH) / (KEYFRAMEWIDTH) + return int(x*factor) + +def unscale_middle(middle): + if not middle: + return None + x_factor = float(TRANSFER_DRAWWIDTH) / DRAWWIDTH + y_factor = float(TRANSFER_DRAWHEIGHT) / DRAWHEIGHT + return (int(middle[0]*x_factor), int(middle[1]*y_factor)) + +# defaults + +STICKS = {'HEAD':(0,15), + 'NECK':(90,15), + 'RIGHT SHOULDER':(185,25), + 'UPPER RIGHT ARM':(60,35), + 'LOWER RIGHT ARM':(35,35), + 'LEFT SHOULDER':(355,25), + 'UPPER LEFT ARM':(300,35), + 'LOWER LEFT ARM':(325,35), + 'TORSO':(270,60), + 'RIGHT HIP':(80,20), + 'UPPER RIGHT LEG':(300,50), + 'LOWER RIGHT LEG':(340,40), + 'RIGHT FOOT':(85,15), + 'LEFT HIP':(280,20), + 'UPPER LEFT LEG':(65,50), + 'LOWER LEFT LEG':(15,40), + 'LEFT FOOT':(275,15)} + +PARTS = {'RIGHT HAND':14, + 'LEFT HAND':14} + +STICKLIST = ['NECK','HEAD','RIGHT SHOULDER','UPPER RIGHT ARM','LOWER RIGHT ARM', + 'LEFT SHOULDER','UPPER LEFT ARM','LOWER LEFT ARM','TORSO', + 'RIGHT HIP','UPPER RIGHT LEG','LOWER RIGHT LEG','RIGHT FOOT', + 'LEFT HIP','UPPER LEFT LEG','LOWER LEFT LEG','LEFT FOOT'] + +LABELLIST = ['HEAD','NECK','RIGHT SHOULDER','UPPER RIGHT ARM','LOWER RIGHT ARM', + 'RIGHT HAND','LEFT SHOULDER','UPPER LEFT ARM','LOWER LEFT ARM','LEFT HAND', + 'TORSO','RIGHT HIP','UPPER RIGHT LEG','LOWER RIGHT LEG','RIGHT FOOT', + 'LEFT HIP','UPPER LEFT LEG','LOWER LEFT LEG','LEFT FOOT'] + +# The joint is the circle at the end of the stick + +JOINTS = {'HEAD':'head', + 'NECK':'neck', + 'RIGHT SHOULDER':'rightshoulder', + 'UPPER RIGHT ARM':'rightelbow', + 'LOWER RIGHT ARM':'righthand', + 'LEFT SHOULDER':'leftshoulder', + 'UPPER LEFT ARM':'leftelbow', + 'LOWER LEFT ARM':'lefthand', + 'TORSO':'groin', + 'RIGHT HIP':'righthip', + 'UPPER RIGHT LEG':'rightknee', + 'LOWER RIGHT LEG':'rightheel', + 'RIGHT FOOT':'righttoe', + 'LEFT HIP':'lefthip', + 'UPPER LEFT LEG':'leftknee', + 'LOWER LEFT LEG':'leftheel', + 'LEFT FOOT':'lefttoe'} + +JOINTTOSTICK = {} +for jname in JOINTS: + JOINTTOSTICK[JOINTS[jname]] = jname + +PARTS = {'HEAD':40, + 'RIGHT HAND':14, + 'LEFT HAND':14} + +TESTSTICKS = {'RIGHT SHOULDER':(37,20), + 'UPPER RIGHT ARM':(6,15), + 'LOWER RIGHT ARM':(10,15)} diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..63e9d64 --- /dev/null +++ b/utils.py @@ -0,0 +1,89 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import gtk + +import sugar +from sugar.graphics import style +from sugar.activity.activity import get_bundle_path + +from theme import * + +def map_range(value, ilower, iupper, olower, oupper): + if value == iupper: + return oupper + return olower + int((oupper-olower+1) * (value-ilower) / + float(iupper-ilower)) + +class TempoSlider(gtk.HBox): + def __init__(self, min_value, max_value): + gtk.HBox.__init__(self) + + self._pixbuf = [None] * 8 + self._image = gtk.Image() + self._image.show() + + # used to store tempo updates while the slider is active + self._delayed = 0 + self._active = False + + self.adjustment = gtk.Adjustment(min_value, min_value, max_value, + (max_value - min_value) / 8, (max_value - min_value) / 8, 0) + self._adjustment_h = self.adjustment.connect('value-changed', + self._changed_cb) + + slider = gtk.HScale(adjustment = self.adjustment) + slider.show() + slider.set_draw_value(False) + slider.connect("button-press-event", self._press_cb) + slider.connect("button-release-event", self._release_cb) + + self.pack_start(slider, True, True) + self.pack_end(self._image, False, False) + + def set_value(self, tempo, quiet = False): + if self._active: + self._delayed = tempo + elif quiet: + self.adjustment.handler_block(self._adjustment_h) + self.adjustment.set_value(tempo) + self._update(tempo) + self.adjustment.handler_unblock(self._adjustment_h) + else: + self.adjustment.set_value(tempo) + + def _changed_cb(self, widget): + self._update(widget.get_value()) + + def _update(self, tempo): + img = map_range(tempo, self.adjustment.lower, + self.adjustment.upper, 0, 7) + + if not self._pixbuf[img]: + self._pixbuf[img] = gtk.gdk.pixbuf_new_from_file_at_size( + os.path.join(get_bundle_path(), 'icons/tempo' + + str(img+1) + '.svg'), + style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE) + + self._image.set_from_pixbuf(self._pixbuf[img]) + + def _press_cb(self, widget, event): + self._active = True + + def _release_cb(self, widget, event): + self._active = False + if self._delayed != 0: + self.set_value(self._delayed, True) + self._delayed = 0 -- cgit v0.9.1