Browse Source

Added animation system

Shuying Feng 15 years ago
parent
commit
b18c13895c

+ 936 - 0
direct/src/leveleditor/AnimControlUI.py

@@ -0,0 +1,936 @@
+"""
+   This is the GUI for the Animation Control Panel
+"""
+from direct.interval.IntervalGlobal import *
+from direct.actor.Actor import *
+from panda3d.core import VBase3,VBase4
+import ObjectGlobals as OG
+import AnimGlobals as AG
+
+import os,wx, time
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+#----------------------------------------------------------------------
+FirstFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACh0lE"
+    "QVRIie2Uz08aQRTHd368CZzYGGNjPWkTlB7EK0QSewSSamL9R+upXmpko/LLskgqhrDGyqb4"
+    "Y40CB3dnd6aHMYBIjSZe2vhOk/2+93nf93Z3UOvsTHvtwK9OfIP+f1CEEKUUITRWJYQQQl4A"
+    "RQgBpQihVqvV6/UwflBCCGGM3XY6tm33H9JhGWPMOR9xFwhhnZzs7e21bPvL+rqu60KIe3eU"
+    "3tzcVCqVQqHwYW5ubW0tCIIBlDFmmmbTsjLptBoEADjnjUajWCodHx/3er2JiQklqWEdxzGr"
+    "1R8HB7/bbU3TotHoqFOMcfv8vFarZdJpxpjruvV6vVAs1ut1z/MopQBACAEAAGi32xXTLJfL"
+    "l5eXGGMAUJ3Gj88Y45w3LSu/v9+0LNd1AYAxpmlaEASE0uvr659HR+Vy2XEcla9pmpRS0zQy"
+    "tOsBFCEkhPi2tZXP533fD4VCqqY/iue6Xzc3r66uGGPDUr98kNw/SSkJIclE4tPKiq7rnHNl"
+    "QYUQIhwOp5aXF+bnlfERKKUDf3RYkFJOTk5ms9n40lJuZ6d6eOh5HgDcu5AyFoslk8liqWQY"
+    "xsXFxcjnOQYqhPB9PwgCzvn76emNjY3FeNzI5ZrNpjKMCQmCAACSicR8NLqfz5dLpU63q9Dk"
+    "sVMhRCQSmZmZQQhJKTnnCKGPsdjc7KxpmoZhtGxbCIEwVr11Xc9mMvHFxZ1crlarqfzBfp++"
+    "+THGlNLbTmd3d/fX6enn1dV3U1O+7ysVAIQQjUbj+/Z2bGEhlUqpXT+AKpuP0WpxjuOEw+FQ"
+    "KDScgxACgG63e3d3F4lEnuV0uFi1HNtVXQjq99VG3v4T8Tecij7uvsczoS+KN+g/Av0D3hpG"
+    "5dYDHHkAAAAASUVORK5CYII=")
+
+
+#----------------------------------------------------------------------
+PreFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACQ0lE"
+    "QVRIieWVzW4aMRSFbV+DCHTDJlkAYZMKAek2fQHYddElu/SteA+eoNk0iPA7BBBQgtQJI6Gi"
+    "ZoQKM2PP2F24P2RmKFGVRauenSX707nH99r4XtfRc4s8O/G/ghKMMcaHoQAAAIdxhFBKGeeM"
+    "MR/XDxVCrFYr0zQP4hzH0TStVqvpuk4p3d3waAEA6/W6Wq2m0ul3l5cIISmlDwcAm81mPp83"
+    "m827+ZwCFAoFn9NHUEXZWpZt20F3GGPLsiaTidbvz2Yzx3EQQvFkkgSy8kPVeUJ+xQIACjcc"
+    "DjudziddZ4ypBIQQyshh6C4OIWSa5sfZrNVqGYZh2zal9GeCUkq19KUUDsUIUUpN0xwOh71e"
+    "b2EYnHNCSCQSCe5EgZYKgWKMGefvr64Gt7f3i4XnebvuQhQoP7z5CSG2ZX15ePA8T2W6D+i7"
+    "gL1OpZQUoFwuF4vFD9fXo9HI2m6B0lA0CZuU8KJUPelM5u3Jyavz80ajMZ1OXdf9vesDUOXX"
+    "c10gpFAsZk5PB4NBvV5fLpdCiF1rT70oH5ozFj86en1x8fLsrN1ud7vdz6sVAKgo1UPxpJby"
+    "SQghhEgmk6VSKZ/PN25u+pq2tSzFCsYRMqacc9d1g2jP8xBCqVTqzfFxsVCo1+vj8Zgx9n2u"
+    "9kGFEIlEolKpJOJxGeg+Jdd1CSG5XC6bzbbabcMwghOFfR8fxjgajQohOOfhWfyQyvTrZgOE"
+    "xGKxXW5I+er5OSghBEboRSIhpfyTi9oniZAMBIr+0o/vH4d+A5itKwKTfnzPAAAAAElFTkSu"
+    "QmCC")
+
+
+#----------------------------------------------------------------------
+PreKeyFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACPUlE"
+    "QVRIib3Vz0/aYBgH8OfHiyTGgAsciJMl6A5j7OLO4+48eHT/qjflSDPlRweNEqPGrTF2hQwW"
+    "gbR9nx2Yi9Ii1bE9x7b55Pu87/Ok+PXqChZdtHDxv6FEsLS0UJQZXVcdHIBSi0OJ0HXV4aHE"
+    "QJmZmGOgML99BCAipZTjOPVard/vE00jT2gTEYkIEV3XbZpms9HwfP/T3l4mk9FaPwdVSmmt"
+    "v7uuaZqmaV5fX2utX66tEbOIPC0pIjIzANi23basRqPR7XaDIGBmRAQACImPoZNmtda2bbda"
+    "raZpOo4zeaiUAgARYebwgUajiJhIJPzR6Jtt12u1k5OTG8f5Y90vVgrjoIQ4Ho/Pz87ax8df"
+    "TLM/GABAIpGIbkckVvtE9PP2dn9//8KyiFkp9fvsogoRI99Ohw+CIJ1K7e7ulsvllZWVIAim"
+    "xuVBIqVinakAIFE+n8/t7LwtlarVauf0dDgaRUeeMQDRt+95HiG+3tx8lc+32m2jWr24vASA"
+    "yfDPCj4HBQAR8X2fmd9vbW0UCs1m8/PRkXNzowH4buUnIxW+qTnDLyKe56VSqQ/l8pti0TCM"
+    "Rr3+o9+fuDwjeKw1DYIAAbKZzMft7WKxaBiGZVnD4XDWHcbdfZnQiBuFwvr6eqfTqVQqAhBe"
+    "/Blo1Hd3b8T3fSZ6Vyrlcrler7eUTIbzhlBEmLU/92jP816srqbTaREJh8UHv2hEGI9xMJBs"
+    "9pG8c+thUhFIJmV5GXz/2SJE/E5E/lKMQhdR/wT9BR51CSZZ1VE7AAAAAElFTkSuQmCC")
+    
+#----------------------------------------------------------------------
+PrePlay = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACV0lE"
+    "QVRIic2Vz1PaUBDHs5tkH9jROv4LDhUHrLXOlOml/p3+H/VYg6YJQz3UWkDDAJlob8J4AUxq"
+    "3tse4lBKEIV6cI9vdz7vuz/eW/h1daU9t+GzE18kFBERpxAWhAJAJpO5u7sbDodp7iJQIjIM"
+    "o16v7+/vtzsdk2giwJgLZxqGYZqXl5eWZf04O4ui6NPeHqTCngpFRCFEr9f76jjVarXf7wsh"
+    "hBBKqXTw41BEFETD21vbtstHR9fX10SUzWaZmZmllHNDiYiZz2q1Q8vyfV9HzGazIy9r2nxQ"
+    "0zR1XQ+CwLKsn7WaUkoQAUwWUEqpMT8O1XWdiLrdrm3b305OBoOBEMI0zSk3M7NSWuqmf6BJ"
+    "NwaDgeO69vFxt9dLyvdQNolSnqEUAKSU309PLcsKgkDX9dm4ETR9aIyIiFgulz8fHEgpl5eX"
+    "ETEtYdyS+iYjBZo2HnoPZWal1IdS6fXqarVaDYKAmWlaZyYsTpQCjLfrb/rM/Gpp6WOp9G57"
+    "u1GvO67rBwErNQP9pDlVSt2Goa7r73d3C8Vio9GoVCrtTkc9gAYAfuKLklJKKRFxZ2enUChc"
+    "eJ7rOK12W0ppEuEYmpnlXM9UKRWGISK+3drazOc9z6tUKl6z+TuOiWj03ck4ngM6jgaAYrG4"
+    "sbHRarVc1/WazTAMiUjTNKnUrDmdYcycqN7M59/kcu1Ox3Hd8/PzMAzjOF7867tXHUUAkMvl"
+    "1tfXfd//cniIACqlFBZb0QBARHEc39zcrKysTHgX3FHMHEWRUmptbS3t/a9tyszxtO6/vL3/"
+    "kP0BhtFQnDqk9wIAAAAASUVORK5CYII=")
+
+#----------------------------------------------------------------------
+Play = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACTUlE"
+    "QVRIidXWzU7bQBAH8JmxLW8SbglXLCchTsKFElEUu4aaHnhSnoInoBeEihoSwodEz5wKya67"
+    "Oz24UAiOgQhV6p5sy/r5v7OztvHH9TW896B3F/8hats2Ir4zenNzQ0Su6y5M249PENG27YOD"
+    "g5+3t192d33fJyKlFDMvjgIAIRrmo6Oj8/PzTqcThWGjXrcsS6WpMWZBlAFs2xZCAMDx8fFg"
+    "MAharTAMm82mEEJK+ZrUsygiWkQAQERCCGPMt5OTwelp0Gr1+/0gCBzHUUoVp55FAcCy/178"
+    "QzN/HwyGo1Gz0QijqB0EQogCOg8lmll3QnRdl5mHo9HZeNyo1/v9frfbFUKkaaq1fhlFotzC"
+    "4T19Nh6PLy58z4vCsLu2VhJCPk2dl9SyCjr0gb64vLy8uvI8b2trKwiCcqn0EOVp8zMDgG1Z"
+    "88THtBDCcZzhcLi/v//18JAeFe1J0uxBRAQAzFy8o5RSWuvV1dUkSYJWyxjzkDR/+sUxtdZK"
+    "qVq1Gm9vf9zcrFQqUsqimiJiAWqMkVJWKpUoDOM4rtVqSqnJZDJz27OkzEgEzybOzEopIvqw"
+    "vp4kied5Wuvn3Bw0L2maptoY3/d3k6Tb6SDidDqdN5s8NGup++OsfMvLy593dnq9XrlUki/t"
+    "0fkoIjNPp9OlpaU4jj9FUbValVJOCgMWoUQkpXRdt7exkSTJysrKrzSdV75XoQyAiHXf39vb"
+    "a7fbiPgmLhs484kmoru7O8dxsu576zs/P6kxplwuA0Dx+r4NzdyFuWz8Pz8TvwFpng4ClVur"
+    "2QAAAABJRU5ErkJggg==")
+ 
+#----------------------------------------------------------------------
+NextKeyFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACNUlE"
+    "QVRIic2Vy27aQBSGj+3xBQNRlSJlQWhwSKPUSMkQmcu+Sl8zW56AVfMCTQgLVEQSbxCELhFi"
+    "wcUzc7qwihQ8NiTNImdny/P5/89tlNFwCO8d6rsTPyAUEXQdCAHE7VBFUXaCmqZ2d6c9PIBh"
+    "bIEi4nK5VFVVVbeYQMMg9/fq4yNGoGRDI2Os2+0yziuVim3bnDGMuFv/P87+JhQBRs/PnU7H"
+    "9/1Go+E4jqHrnPNYtCzIxrMCYOg6IPZ6vcFgUHbdWr1+mM8DAOf8jdC1ZELIYrH4dXv75PtV"
+    "z6OUfs7lBOdCiFdDETFgLLQalms6nf68ufnd63med3F+nk6nhRDJaEn18eUBTdNUVR2Px61W"
+    "q9ls9vt9xhghJKHzJDmNRpgNRHzy/eFo5LpurVr9Ui4rMW0XsQ8QZy1Er1ardrvt+75bq32f"
+    "TD4dHES/lNgPGJNC12wAmM/nf8bj2WwmFSuvvjTC+pim+bVUopSeXl5mr6+5TMFO0DXupFSi"
+    "lJaOj9OZDLMsBUA6ErKWWq3g31oRQnDObdt2HKdRrxcKBdu2OedBEERHPklpOJGhumwmUzo5"
+    "oZQWj44syxJCBEGw1ZkEyjhnjNm2/e3s7ILSYrFo6LoQgiUXMBmaSqUqlHrV6mE+b1kWY2x3"
+    "nASKiLqu/7i6yu7tWaYZ5i7pNGJ070mgmqblcrldzRICmhZ9rbz9ilZVZTIBQjCbhZdD+Irm"
+    "3wwhcH8fECEy1v8BBYCYtf3R7v34+AuNHRpDa7trrwAAAABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+NextFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACOUlE"
+    "QVRIidWVS2/aQBSFZ8ZjYxkTFmSBRFAgpk1CJTIgE/orq/wMJFbNAjbNLqsgxAKVh+INgrhL"
+    "HkK8PI8urCKCx01bsWju+vrTuef6noHj0Qgcu9DRie8dCiE8MlQIsdlsEEIIvT0EQkhV1WAn"
+    "PtBIKe10OpSxYrFoGAajVAghJUIIF4vFdDqNx+OxWIxzLlcKIRQAjF9eGo1GrVYbDAaMc4yx"
+    "1BBN03q93pe7u3a7HdG0UKUAAAiApqpAiG63OxwOP+Xzt5XKWSoFAGCMHTRzzj3P29coh+4k"
+    "Y4zX6/VTs/nsOGXbJoQkTk85Y/sICCFCKDjHIVQI4VHqu+ivazabfXt4+N7t2rZ9UyhEo1HO"
+    "eVDdfkm2L15/oCgKQsh13Xq9Xq1W+/0+pTTMaLlSaaPvhhDi2XFG43E+n78tlz9eXob9doHx"
+    "AQgbzUdvt9tWq+U4TqlUAkJIuXJPpdAdGwCwWq1+uK5pmkhmgnz70vL3E4lEPlgWIaRQKDw1"
+    "m/Tx8R+hO1zOsggh1sVF1DQxxmGbko2/3YJfscI5Z4wZhpHNZj9XKul02jAMxpjneb8JB4lS"
+    "/9h9dTHTtHI5Qkjm/FzXdf+Egs1vQyljlFLDMK6vrm4IyWQymqpyzmlggUIIznmQC/ffKITQ"
+    "crn8en+PFcUul89SKV3XaUhQIYQmk4nruslkMpFIvDrffagfffP5PHZyokcijLGw3NtxMcaM"
+    "sYOsgcHXVFEU6VB/XhJPgxH3t/WuX9P/FPoTFHYunoRw1IsAAAAASUVORK5CYII=")
+    
+#----------------------------------------------------------------------
+LastFrame = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAClElE"
+    "QVRIie2VT0sbQRjG950/Gw8mW4QgCWxaMSdjwkq60YDmYD+A/QBiP5keq2IuHuKxxUNFqoI2"
+    "UuOlInahHpYkHhLdmXmnh5VNiLFS8CL4nhZm3t/zzvMsM/D76sp47iLPTnyFvkQoABDyqAYh"
+    "BAD+DwoA3W632WwyxiilA5u01r7vCyE454+B+iXvoZTSVru9Va1++fq10+2aphltAgAA+H5w"
+    "sL6xcXl5yRhjjD2Eaq0HoWHzzc1NrVZbXV09OjoytOacR2iNeHp6ura2tr297ft+LBaLvOKc"
+    "Hx4efl5fD+UNw+hpIiIhhHPued5WtXpycjI/P5+dnARChBBaa9M0AyG+7e01zs/nZmeLxWIi"
+    "kZBSEkJarZbneRGqB9WIiAgAjDGt9c+zs18XF47jVBYWkskkIhqGQQgxTbPZatV2dn7U65VK"
+    "JTc1xTkf8GSIO6EVpmlKKff39xuNhuu6t7e3kRWMUoNSz/M2Nzez2eyHxUWlVH9QLKIopcJx"
+    "en4TwhjzfX93d/eNZVFK+9NgjAkh6vX63d2dlUjQvt+x96W17u9BxCAIGGMzMzPLy8uZTEZK"
+    "Ga1KKYUQ4+PjH5eWPq2sJJNJqdSQ4yOi1toAQEQp5cjISCGfL5VK7yYmCMDx8XEoHKLTqZTr"
+    "uvl83rIsxphSKgiCYVCtpZQiCOLxuOM4ruu+zWQopUqpQIhwNM65bdvvi8VcLmdZllQqCAJE"
+    "TKVS07ncEKhSKmaaJdedK5cztg0AIQgAtNaolG3b5XI5Pz09OjoqhIhGk1IWCgXHcYQQ9wmF"
+    "zwkhpN1udzqddDodhjbg75/r67GxsUQ8LqUcyPNhQfRGEUIIIQO43okYQ8QncQ88/WdPf/RP"
+    "1ku8pF+hz1V/AVdHWFTfbzsRAAAAAElFTkSuQmCC")
+    
+#----------------------------------------------------------------------
+Key = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACl0lE"
+    "QVRIie2Uv0sbYRjH3x+X9/JGk9wlYkg6icbJMf0LHCxo/QuKUCfR0TFqB/dKHSztYKHBYHCp"
+    "kIIFqZM6ODho3Y2tQ44EMRffu3tz79vhlcNGC5I4+h2/L3yen+8Df19cgKcWenLiM7RbKITw"
+    "vql1g6OUAikhQowxIUTw1GGmEEKdkEKh8GZq6v3KCgAAY9wttKen58Pqan5hwTCMcrm8sLhI"
+    "COkKSgg5Ozv79PlzcWPjy/r6j50du9HY3d2llHYO1XV9s1R6NTY2OjpqWRbGeG5u7uDwMGjr"
+    "LRRCiB4tz/N+nZ6OT0y4rgsAcBwnm82GNK1WqyGEQDB9IYTv+wAAhBDGmHOuaZqUUpl3hRBq"
+    "NBp2szk0OOh5HgBASkkpTSSTVcsyTVMIoakeHR8fb5ZKGsYN2242m5l0ulqtYozNROLurqia"
+    "GGPX19eGYUgplSmljEQiruOotdUAAJzz4eHh+fl5Sun3cvnn3t7y8nI+n0/29c3MzDRtuz1T"
+    "256dnXUcJxKJBJG45yGEVBhNxSGE6LpOKTUMIxqNJpPJWCwWi8USpklCoTZoPB4nhPy5vOzv"
+    "71f98X2/Vq8bhqHKQkH+QgghBOfccRwhhOu6nucFfiDf9ymlqVTqYH8/HA4DADDGlmVdXV2l"
+    "Uql/oEq+72cymZe5HGNsZGQkOzTEOW8blCpwYnz8a6FQr9ej0ahpmltbWy8ymd7eXvUK2y4/"
+    "xhhj7LouIURKyTm/fzIQQhDC15OTmqa9W1o6Ojr6tr1dLBaNeLzVaj0AfaR0Xa9UKm+np09O"
+    "TgYGBj6ureVyOcbY7dw6g0opw+Fw8+amcn6eTqdN02SMBTV1CFVCCIVCoVar1fZHOr+nAAC1"
+    "JA8E6wb6Pz1Dn15/AQsQSZkYzgBNAAAAAElFTkSuQmCC")
+    
+#----------------------------------------------------------------------
+Stop = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAAvklE"
+    "QVRIie3VMQ6DMAwF0G9CEUxMzAwch4NwCEbuxyXoygiBkMQd2KoUM7Cg4tFSnixZ/qH3MODq"
+    "ii4XHzQO9IiQpvJTIlgLY06gRFgW3/dwDkRHqHMoiqiq4L2ExjGPo+k6TBOUOkK1VnUdtS2M"
+    "AfMhug+bZfBeQAEkSbB9n+0/6IPeAQ2dKTO0htbi7QcjKoQ6hzx/NQ22TUgpa6ksYe1XmgCg"
+    "wMe3B4pYe56u64lJATBjnmX0d91n+3+OfgDGM0GplgNFhQAAAABJRU5ErkJggg==")
+#----------------------------------------------------------------------
+DeleteKey = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAEXUlE"
+    "QVRIibWWX0hUWRzHf+ece4/3TjPjvTMOrqM+lH9KmQhEszCC2OifLRRF+LKwb8X66Eug0UOP"
+    "Cz22+7JIGwxmEGmTWUahkhapLQSbwQZFm6iDOubM3DPn3nvOPozT6jhKQft7u/f+zuf3O7/v"
+    "+f3ORf98+ADf2vA3JxaC4q8Mg9DGJeufpUSWBYryxSlhcBy0spLHXQ9VFOXxY/z2LWjal+Qo"
+    "CaGxGI7HgZDNoQDOwYPqyAianQVV3ZopNY0+eCBDIXfXLrDtzaGuK02THztGYzFIp/PiryPq"
+    "Oh0f34ax0tq6jRC81fYBgHNRWens30/v3AEorJvUNPXNG/39+98TiR/b2q5evQoAZE0GBdYg"
+    "xpxIRFRV0f5+qaqA0LrPqorn5oqnpn55966zq8vw+WL37nVdukQp3QoKAMiy7JYW6fHQoSG5"
+    "VjRCgDHfw4d/VlT81tMTvX69u7v7weBgcmXl0aNHuq5vBc3my48eRcvLyvPnMuuNEGBM+/pw"
+    "S0t0bOz4oUPfHz4cj8cJIe3t7WPj40KIdVCEEM4zhLAQzqlT6vS0Oj0Nug6aVjQ4iCoqUvX1"
+    "f01Otp48mclkAIAxVlNToyrKwsJCVrFVqBCCc845dxxHSsk5F0II2+aOk2xtxcPD7uwsfvbM"
+    "tizrwIHEx4/JVKq6qopzDgBSSk3TAsHgfDyelUsBAErpy5cve27eVAhZSSZTqVS4rGx+fp4Q"
+    "YgYCLsZFyeQPg4NzlD7dvh2mpqx0+tOnT4ZhSClXz4OUHo8nwxhCaBVq23ZtbW1HR4eu6/di"
+    "scdPnly5cqWzszNYUnLh/Pkk57C0FIhGeSjUdOYMwXhlefnn9nbGmMfj+Vw9m3OMcTYMzsah"
+    "lAZMM2CahmH4fL5gMOj3+/1+fyAUCup6eHQULlzQmpu/GxkxQqGKykpK6ceZGSU3JVzXXVhc"
+    "NAwjqxX+nL8Q2TLajDEhRCaT4bYtAZTbtzMNDSIUshsaXK8XDwxohlFaWjr29KmmaQBACInH"
+    "44lEorS0dB30c8BwONzU2GhZViQSqY5E0N27orzc2bMHGEOM8SNHUCqljI+3nj37x40bi4uL"
+    "Pp/PNM1bt26Vh8Nerze7fZQ3+QkhhJBMJkOLi8noqDszw0+fRozlegUDgNbbK5qaTly8qNj2"
+    "5cuXX7x4caevLxqNGsXFjuMUgK5Wo6hIef1amZxkbW1ICMipvNpUluXv6/u7vv6nrq5XExPb"
+    "d+z49dq1xsZGy7JWdSsAVVU0N0cHBjLnzoGug+sWcJif9w4NJU6ceL+0VFZSYpqmZVkoNyU2"
+    "tCkhkE7T+/f58ePS6y1ABADbFmVlqX37vAMDddXVXr+f5U5oIShCAED7+53mZlFejjgvQMw6"
+    "Mubu3GnX1cneXjfX8ptAVVUdHhaVlc7u3f+JsxnXsuy9e2VJiTIxAWvmHuTXFCG0vCwNAzYE"
+    "39QwRum01PW1Yubfpl9HBAAh8ogboABfR8ylkvfif/lD+Rcv7QbV/D7nwgAAAABJRU5ErkJg"
+    "gg==")    
+    
+class TimeSlider(wx.Window):
+    """
+    This is the Time Slider Panel.
+    """
+    def __init__(self, parent, slidersize, sliderStartFrame, sliderEndFrame, curFrame):
+        wx.Window.__init__(self, parent, size = slidersize, style = wx.SUNKEN_BORDER)
+    
+        self._mainDialog = wx.GetTopLevelParent(self)
+        self._mouseIn = False
+        self.points = []
+        self.numbers = []
+        
+        self.curFrame = curFrame
+        self.sliderStartFrame = sliderStartFrame
+        self.sliderEndFrame = sliderEndFrame
+        self.frameNum = self.sliderEndFrame-self.sliderStartFrame+1
+        
+        self.InitBuffer()
+        
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+        
+    def InitBuffer(self):
+        self.w,self.h = self.GetClientSize()
+        self.buffer = wx.EmptyBitmap(self.w, self.h)
+        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+        self.DrawTimeSlider(dc)
+        self.DrawNumber(dc)
+        self.DrawFrame(dc)
+        self.DrawKeys(dc)
+        
+    def SetTimeSliderData(self, sliderStartFrame = 1, sliderEndFrame = 24, curFrame = 0):
+        self.curFrame = curFrame
+        self.sliderStartFrame = sliderStartFrame
+        self.sliderEndFrame = sliderEndFrame
+        self.frameNum = self.sliderEndFrame-self.sliderStartFrame+1
+        
+        self.points = []
+        self.numbers = []
+        
+        self.InitBuffer()
+        self.Refresh()
+        
+    def OnPaint(self, evt):
+        dc = wx.BufferedPaintDC(self, self.buffer)
+    
+    def DrawTimeSlider(self,dc):
+        
+        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+        dc.Clear()
+        
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
+        
+        self.unitWidth = self.w/float(self.frameNum)
+        
+        if self.frameNum <= 20:
+            
+            self.points.append(((float(0),self.h),(float(0),self.h-15)))
+            
+            for i in range(1,self.frameNum):
+                temp = self.points[i-1][0][0]+self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+                
+            for i in range(self.frameNum):
+                self.numbers.append(self.sliderStartFrame+i)
+            
+            for i in range(self.frameNum):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 70:
+        
+            self.points.append(((self.unitWidth,self.h),(self.unitWidth,self.h-15)))
+        
+            for i in range(1,int((self.frameNum+1)/2)):
+                temp = self.points[i-1][0][0]+2*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/2+1):
+                self.numbers.append(self.sliderStartFrame-1+i*2)
+        
+            for i in range(int((self.frameNum+1)/2)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/2):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 150:
+            
+            self.points.append(((self.unitWidth*4.0,self.h),(self.unitWidth*4.0,self.h-15)))
+            
+            for i in range(1,int(self.frameNum/5)):
+                temp = self.points[i-1][0][0]+5*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/5+1):
+                self.numbers.append(self.sliderStartFrame-1+i*5)
+        
+            for i in range(int(self.frameNum/5)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/5):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 250:
+            
+            self.points.append(((self.unitWidth*9.0,self.h),(self.unitWidth*9.0,self.h-15)))
+            
+            for i in range(1,int(self.frameNum/10)):
+                temp = self.points[i-1][0][0]+10*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/10+1):
+                self.numbers.append(self.sliderStartFrame+i*10)
+        
+            for i in range(int(self.frameNum/10)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/10):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 1000:
+            
+            self.points.append(((self.unitWidth*49.0,self.h),(self.unitWidth*49.0,self.h-15)))
+            
+            for i in range(1,int(self.frameNum/50)):
+                temp = self.points[i-1][0][0]+50*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/50+1):
+                self.numbers.append(self.sliderStartFrame-1+i*50)
+        
+            for i in range(int(self.frameNum/50)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/50):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 2000:
+            
+            self.points.append(((self.unitWidth*99.0,self.h),(self.unitWidth*99.0,self.h-15)))
+            
+            for i in range(1,int(self.frameNum/100)):
+                temp = self.points[i-1][0][0]+100*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/100+1):
+                self.numbers.append(self.sliderStartFrame-1+i*100)
+        
+            for i in range(int(self.frameNum/100)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/100):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+                
+        elif self.frameNum <= 10000:
+            
+            self.points.append(((self.unitWidth*999.0,self.h),(self.unitWidth*999.0,self.h-15)))
+            
+            for i in range(1,int(self.frameNum/1000)):
+                temp = self.points[i-1][0][0]+1000*self.unitWidth
+                self.points.append(((temp,self.h),(temp,self.h-15)))
+        
+            for i in range(1,self.frameNum/1000+1):
+                self.numbers.append(self.sliderStartFrame-1+i*1000)
+        
+            for i in range(int(self.frameNum/1000)):
+                dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1])
+                
+            for i in range(self.frameNum/1000):   
+                st = str(self.numbers[i])
+                tw,th = dc.GetTextExtent(st)
+                dc.DrawText(st, self.points[i][0][0]+2, 0.5)
+        
+        else:
+            pass
+        
+            
+    def DrawNumber(self, dc):
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
+        
+        i = self.curFrame-self.sliderStartFrame
+        st = str(self.curFrame)
+        tw,th = dc.GetTextExtent(st)
+        dc.DrawText(st, float(self.unitWidth)*float(i)+2, self.h-th-0.5)
+            
+    def DrawFrame(self, dc):
+        i = self.curFrame-self.sliderStartFrame
+        pos = float(self.unitWidth)*float(i)
+        self.curRect = wx.Rect(pos, float(0), self.unitWidth, self.h) 
+        
+        oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
+                
+        gray = wx.Colour(200, 200, 200)
+        grayPen = wx.Pen(gray)
+        grayBrush = wx.Brush(gray)
+        dc.SetPen(grayPen)
+        dc.SetBrush(grayBrush)
+        dc.SetLogicalFunction(wx.XOR)
+        dc.DrawRectangleRect(self.curRect)
+                
+        dc.SetPen(oldPen)
+        dc.SetBrush(oldBrush)
+        dc.SetLogicalFunction(oldMode)
+        
+    def DrawKeys(self, dc):
+        if len(self._mainDialog.keys) != 0:
+            for key in self._mainDialog.keys:
+                keyFrame = key
+                i = keyFrame-self.sliderStartFrame
+                pos = float(self.unitWidth)*float(i)
+            
+                oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
+            
+                dc.SetPen(wx.Pen("red"))
+                dc.SetBrush(wx.Brush("red"))
+                dc.SetLogicalFunction(wx.AND)
+                dc.DrawLine(pos, float(0), pos, self.h)
+                
+                dc.SetPen(oldPen)
+                dc.SetBrush(oldBrush)
+                dc.SetLogicalFunction(oldMode)
+        else:
+            pass
+        
+    def OnSize(self,evt):
+        self.InitBuffer()
+    
+    def OnLeftDown(self,evt):
+        point = (evt.GetX(), evt.GetY())
+        
+        if point[1]>= float(0) and point[1]<= (float(self.h)-2.0):
+            if point[0]>= float(0) and point[0]<= float(self.w):
+                self._mouseIn = True
+                
+        if self._mouseIn:
+            self.CaptureMouse()
+            self.curFrame = int(float(point[0])/self.unitWidth)+self.sliderStartFrame
+            self._mainDialog.curFrame = self.curFrame
+            self._mainDialog.curFrameSpin.SetValue(self.curFrame)
+            self._mainDialog.OnAnimation(self.curFrame)
+            self.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+        
+    def OnLeftUp(self,evt):
+        if self.GetCapture():
+            self.ReleaseMouse()
+            self._mouseIn = False
+    
+    def OnMotion(self,evt):
+        self._mouseIn = False
+        if evt.Dragging() and evt.LeftIsDown():
+            point = (evt.GetX(), evt.GetY())
+            if point[1]>= float(0) and point[1]<= (float(self.h)-2.0):
+                if point[0]>= float(0) and point[0]<= float(self.w):
+                    self._mouseIn = True
+            
+            if self._mouseIn:
+                self.curFrame = int(float(point[0])/self.unitWidth)+self.sliderStartFrame
+                self._mainDialog.curFrame = self.curFrame
+                self._mainDialog.curFrameSpin.SetValue(self.curFrame)
+                self._mainDialog.OnAnimation(self.curFrame)
+                self.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+            
+        evt.Skip()
+        self._mouseIn = False 
+    
+class TimeRange(wx.Window):
+    """
+    This is the Time Range Panel.
+    """
+    def __init__(self, parent, rangesize, startFrame, endFrame, sliderStartFrame, sliderEndFrame):
+        wx.Window.__init__(self, parent, size = rangesize, style = wx.SUNKEN_BORDER)
+    
+        self._mainDialog = wx.GetTopLevelParent(self)
+        
+        self._mouseIn = False
+        
+        self.startFrame = startFrame
+        self.endFrame = endFrame
+        self.sliderStartFrame = sliderStartFrame
+        self.sliderEndFrame = sliderEndFrame
+        self.frameNum = self.endFrame-self.startFrame+1
+        
+        self.InitBuffer()
+        
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+    
+    def InitBuffer(self):
+        self.w,self.h = self.GetClientSize()
+        self.buffer = wx.EmptyBitmap(self.w, self.h)
+        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+        self.DrawTimeRange(dc)
+
+    def SetTimeRangeData(self, startFrame = 1, endFrame = 24, sliderStartFrame = 1, sliderEndFrame = 24):
+        self.startFrame = startFrame
+        self.endFrame = endFrame
+        self.sliderStartFrame = sliderStartFrame
+        self.sliderEndFrame = sliderEndFrame
+        self.frameNum = self.endFrame-self.startFrame+1
+        
+        self.InitBuffer()
+        self.Refresh()
+        
+    def OnPaint(self, evt):
+        dc = wx.BufferedPaintDC(self, self.buffer)
+    
+    def DrawTimeRange(self,dc):
+        dc.SetBackground(wx.Brush(wx.Colour(150, 150, 150)))
+        dc.Clear()
+        
+        dc.SetPen(wx.Pen(self.GetBackgroundColour()))
+        dc.SetBrush(wx.Brush(self.GetBackgroundColour()))
+
+        self.unitWidth = (self.w-6.0)/float(self.frameNum)
+        self.rangePosX = 3.0+(float(self.sliderStartFrame-self.startFrame))*self.unitWidth
+        self.rangePosY = 2.0
+        self.rangeWidth = float(self.sliderEndFrame-self.sliderStartFrame+1)*self.unitWidth
+        self.rangeHeight = self.h-4.0
+        self.curRect = wx.Rect(self.rangePosX, self.rangePosY, self.rangeWidth, self.rangeHeight)
+
+        dc.DrawRoundedRectangleRect(self.curRect, radius = 2)
+        
+    def OnSize(self,evt):
+        self.InitBuffer()
+    
+    def OnLeftDown(self,evt):
+        point = (evt.GetX(), evt.GetY())
+        self.pos = 0
+        
+        if point[1]>= self.rangePosY and point[1]<= self.rangePosY+self.rangeHeight:
+            if point[0]>= self.rangePosX and point[0]<= self.rangePosX+self.rangeWidth:
+                self._mouseIn = True
+                
+        if self._mouseIn:
+            self.CaptureMouse()
+            self.pos = point
+        
+    def OnLeftUp(self,evt):
+        if self.GetCapture():
+            self.ReleaseMouse()
+            self._mouseIn = False
+    
+    def OnMotion(self,evt):
+        self._mouseIn = False
+        if evt.Dragging() and evt.LeftIsDown():
+            newPos = (evt.GetX(), evt.GetY())
+            if newPos[1]>= self.rangePosY and newPos[1]<= self.rangePosY+self.rangeHeight:
+                if newPos[0]>= self.rangePosX and newPos[0]<= self.rangePosX+self.rangeWidth:
+                    self._mouseIn = True
+            
+            if self._mouseIn:
+                if newPos[0] == self.pos[0]:
+                    evt.Skip()
+                    self._mouseIn = False
+                if newPos[0] > self.pos[0]:
+                    if float(newPos[0]-self.pos[0]) >= self.unitWidth:
+                        if self.sliderEndFrame < self.endFrame:
+                            self.sliderStartFrame += 1
+                            self.sliderEndFrame += 1
+                            self.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame)
+                            self.MainPanelUpdate()
+                            self.pos = newPos
+                            evt.Skip()
+                            self._mouseIn = False 
+                if newPos[0] < self.pos[0]:
+                    if float(self.pos[0]-newPos[0]) >= self.unitWidth:
+                        if self.sliderStartFrame > self.startFrame:
+                            self.sliderStartFrame -=1
+                            self.sliderEndFrame -=1
+                            self.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame)
+                            self.MainPanelUpdate()
+                            self.pos = newPos
+                            evt.Skip()
+                            self._mouseIn = False 
+        evt.Skip()
+        self._mouseIn = False 
+        
+    def MainPanelUpdate(self):
+        self._mainDialog.sliderStartFrame = self.sliderStartFrame
+        self._mainDialog.sliderEndFrame = self.sliderEndFrame
+        self._mainDialog.timeSliderStartSpin.SetValue(self.sliderStartFrame)
+        self._mainDialog.timeSliderEndSpin.SetValue(self.sliderEndFrame)
+        self._mainDialog.timeSlider.SetTimeSliderData(self._mainDialog.sliderStartFrame, self._mainDialog.sliderEndFrame, self._mainDialog.curFrame)
+        
+    
+class AnimControlUI(wx.Dialog):
+    """
+    This is the Animation Control main class implementation.
+    """
+    def __init__(self, parent, editor):
+        wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Animation Controller",
+                           pos=wx.DefaultPosition, size=(920, 110))
+        
+        self.editor = editor
+        self._initOver = False
+        
+        self.parallel = []
+        if self.editor.animMgr.keyFramesInfo != []:
+            self.editor.animMgr.generateKeyFrames()
+        self.keys = self.editor.animMgr.keyFrames
+        self.editor.objectMgr.findActors(render)
+        self.editor.objectMgr.findNodes(render)
+        
+        self.prePlay = False
+        self.play = False
+        self.stop = True
+        
+        self.curFrame = 1
+        self.startFrame = 1
+        self.sliderStartFrame = 1
+        self.endFrame = 24
+        self.sliderEndFrame = 24
+                           
+        self.mainPanel1 = wx.Panel(self, -1)
+        
+        self.timeSlider = TimeSlider(self.mainPanel1, wx.Size(560, 35), self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+        self.curFrameSpin = wx.SpinCtrl(self.mainPanel1, -1, "",size = (70,25),min=self.startFrame, max=self.endFrame)
+        
+        bmpFirstFrame = FirstFrame.GetBitmap()
+        bmpPreFrame = PreFrame.GetBitmap()
+        bmpPreKeyFrame = PreKeyFrame.GetBitmap()
+        self.bmpPrePlay = PrePlay.GetBitmap()
+        self.bmpPlay = Play.GetBitmap()
+        bmpNextKeyFrame = NextKeyFrame.GetBitmap()
+        bmpNextFrame = NextFrame.GetBitmap()
+        bmpLastFrame = LastFrame.GetBitmap()
+        bmpKey = Key.GetBitmap()
+        self.bmpStop = Stop.GetBitmap()
+        bmpDeleteKey = DeleteKey.GetBitmap()
+        
+        self.buttonFirstFrame = wx.BitmapButton(self.mainPanel1, -1, bmpFirstFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonPreFrame = wx.BitmapButton(self.mainPanel1, -1, bmpPreFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonPreKeyFrame = wx.BitmapButton(self.mainPanel1, -1, bmpPreKeyFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPrePlay, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPlay, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonNextKeyFrame = wx.BitmapButton(self.mainPanel1, -1, bmpNextKeyFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonNextFrame = wx.BitmapButton(self.mainPanel1, -1, bmpNextFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonLastFrame = wx.BitmapButton(self.mainPanel1, -1, bmpLastFrame, size = (30,30),style = wx.BU_AUTODRAW)
+        
+        self.mainPanel2 = wx.Panel(self, -1)
+        
+        self.timeStartSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=0, max=self.sliderEndFrame)
+        self.timeSliderStartSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.startFrame, max=self.sliderEndFrame)
+        self.timeRange = TimeRange(self.mainPanel2, wx.Size(450, 25), self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame)
+        self.timeSliderEndSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.sliderStartFrame, max=self.endFrame)
+        self.timeEndSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.sliderStartFrame, max=10000)
+        self.buttonDeleteKey = wx.BitmapButton(self.mainPanel2, -1, bmpDeleteKey, size = (30,30),style = wx.BU_AUTODRAW)
+        
+        self.SetProperties()
+        self.DoLayout()
+        
+        self.Bind(wx.EVT_SPINCTRL, self.OnCurrentTime, self.curFrameSpin)
+        
+        self.Bind(wx.EVT_BUTTON, self.OnFirstFrame, self.buttonFirstFrame)
+        self.Bind(wx.EVT_BUTTON, self.OnPreFrame, self.buttonPreFrame)
+        self.Bind(wx.EVT_BUTTON, self.OnPreKeyFrame, self.buttonPreKeyFrame)
+        self.Bind(wx.EVT_BUTTON, self.OnPrePlay, self.buttonPrePlay)
+        self.Bind(wx.EVT_BUTTON, self.OnPlay, self.buttonPlay)
+        self.Bind(wx.EVT_BUTTON, self.OnNextKeyFrame, self.buttonNextKeyFrame)
+        self.Bind(wx.EVT_BUTTON, self.OnNextFrame, self.buttonNextFrame)
+        self.Bind(wx.EVT_BUTTON, self.OnLastFrame, self.buttonLastFrame)
+        
+        self.Bind(wx.EVT_SPINCTRL, self.OnTimeStartSpin, self.timeStartSpin)
+        self.Bind(wx.EVT_SPINCTRL, self.OnTimeSliderStartSpin, self.timeSliderStartSpin)
+        self.Bind(wx.EVT_SPINCTRL, self.OnTimeSliderEndSpin, self.timeSliderEndSpin)
+        self.Bind(wx.EVT_SPINCTRL, self.OnTimeEndSpin, self.timeEndSpin)
+        
+        self.Bind(wx.EVT_BUTTON, self.OnDeleteKey, self.buttonDeleteKey)
+        
+        self.Bind(wx.EVT_CLOSE, self.OnExit)
+    
+        self.OnPropKey()
+        self.OnAnimation(self.curFrame)
+        
+        self.timeUnit = float(1)/float(24)*float(1000)
+        self.timer = wx.Timer(self)
+        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
+
+        self._initOver = True
+        
+    def SetProperties(self):
+        self.curFrameSpin.SetValue(self.curFrame)
+        self.timeStartSpin.SetValue(self.startFrame)
+        self.timeSliderStartSpin.SetValue(self.sliderStartFrame)
+        self.timeSliderEndSpin.SetValue(self.sliderEndFrame)
+        self.timeEndSpin.SetValue(self.endFrame)
+    
+    def DoLayout(self):
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer1 = wx.FlexGridSizer(1, 10, 0, 0)
+        mainSizer2 = wx.FlexGridSizer(1, 6, 0, 0)
+        
+        mainSizer1.Add(self.timeSlider, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
+        mainSizer1.Add(self.curFrameSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 3)
+        
+        mainSizer1.Add(self.buttonFirstFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonPreFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonPreKeyFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonPrePlay, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonPlay, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonNextKeyFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonNextFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        mainSizer1.Add(self.buttonLastFrame, 0, wx.ALIGN_CENTER_VERTICAL)
+        
+        mainSizer2.Add(self.timeStartSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer2.Add(self.timeSliderStartSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer2.Add(self.timeRange, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer2.Add(self.timeSliderEndSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer2.Add(self.timeEndSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer2.Add(self.buttonDeleteKey, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
+        
+        self.mainPanel1.SetSizerAndFit(mainSizer1)
+        self.mainPanel2.SetSizerAndFit(mainSizer2)
+        
+        dialogSizer.Add(self.mainPanel1, 1, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 5)
+        dialogSizer.Add(self.mainPanel2, 1, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 7)
+        
+        self.SetSizer(dialogSizer)
+        self.Layout()
+
+        self.dialogSizer = dialogSizer
+        
+    def OnCurrentTime(self, evt):
+        self.curFrame = evt.GetInt()
+        self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+        self.OnAnimation(self.curFrame)
+    
+    def OnControl(self):
+        self.curFrameSpin.SetValue(self.curFrame)
+        self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+        self.OnAnimation(self.curFrame)
+        
+    def OnFirstFrame(self,evt):
+        self.curFrame = self.sliderStartFrame
+        self.OnControl()
+    
+    def OnPreFrame(self,evt):
+        if self.curFrame-1 >= self.startFrame:
+            self.curFrame -= 1
+            self.OnControl()
+        else:
+            evt.Skip()
+    
+    def sortKey(self):
+        for i in range(0, len(self.keys)-1):
+            for j in range(i+1, len(self.keys)):
+                if self.keys[i]>self.keys[j]:
+                    temp = self.keys[i]
+                    self.keys[i] = self.keys[j]
+                    self.keys[j] = temp
+        
+    def OnPreKeyFrame(self,evt):
+        self.sortKey()
+        if self.curFrame <= self.keys[0] or self.curFrame > self.keys[len(self.keys)-1]:
+            self.curFrame = self.keys[len(self.keys)-1]
+        else:
+            for i in range(1, len(self.keys)):
+                if self.curFrame <= self.keys[i] and self.curFrame > self.keys[i-1]:
+                    self.curFrame = self.keys[i-1]
+                    break
+        self.OnControl()            
+            
+    def OnTimer(self,evt):
+        if self.prePlay == True and self.stop == False and self.play == False:
+            if self.curFrame-1>=self.sliderStartFrame:
+                self.curFrame -= 1
+                self.OnControl()
+            elif self.curFrame == self.sliderStartFrame:
+                self.curFrame = self.sliderEndFrame
+                self.OnControl()
+                
+        if self.play == True and self.stop == False and self.prePlay == False:
+            if self.curFrame+1<=self.sliderEndFrame:
+                self.curFrame += 1
+                self.OnControl()
+            elif self.curFrame == self.sliderEndFrame:
+                self.curFrame = self.sliderStartFrame
+                self.OnControl()
+            
+    def OnPrePlay(self,evt):
+        if self.prePlay == False and self.stop == True and self.play == False:
+            self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpStop, size = (30,30),style = wx.BU_AUTODRAW)
+            self.DoLayout()
+            self.prePlay = True
+            self.stop = False
+            self.timer.Start(self.timeUnit)
+            evt.Skip()
+            
+        elif self.prePlay == True and self.stop == False and self.play == False:
+                self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPrePlay, size = (30,30),style = wx.BU_AUTODRAW)
+                self.DoLayout()
+                self.prePlay = False
+                self.stop = True
+                self.timer.Stop()
+                evt.Skip()
+        else:
+            evt.Skip()
+    
+    def OnPlay(self,evt):
+        if self.play == False and self.stop == True and self.prePlay == False:
+            self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpStop, size = (30,30),style = wx.BU_AUTODRAW)
+            self.DoLayout()
+            self.play = True
+            self.stop = False
+            self.timer.Start(self.timeUnit)
+            evt.Skip()
+            
+        elif self.play == True and self.stop == False and self.prePlay == False:
+                self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPlay, size = (30,30),style = wx.BU_AUTODRAW)
+                self.DoLayout()
+                self.play = False
+                self.stop = True
+                self.timer.Stop()
+                evt.Skip()
+        else:
+            evt.Skip()
+    
+    def OnNextKeyFrame(self,evt):
+        self.sortKey()
+        if self.curFrame < self.keys[0] or self.curFrame >= self.keys[len(self.keys)-1]:
+            self.curFrame = self.keys[0]
+        else:
+            for i in range(0, len(self.keys)-1):
+                if self.curFrame >= self.keys[i] and self.curFrame < self.keys[i+1]:
+                    self.curFrame = self.keys[i+1]
+                    break
+        self.OnControl()  
+    
+    def OnNextFrame(self,evt):
+        if self.curFrame+1 <= self.endFrame:
+            self.curFrame += 1
+            self.OnControl()
+        else:
+            evt.Skip()
+    
+    def OnLastFrame(self,evt):
+        self.curFrame = self.sliderEndFrame
+        self.OnControl()
+    
+    def OnTime(self):
+        preFrame = self.curFrame
+        self.curFrameSpin.SetRange(self.startFrame,self.endFrame)
+        self.curFrame = preFrame
+        self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+        self.timeRange.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame)
+        self.parallel = self.editor.animMgr.createParallel(self.startFrame,self.endFrame)
+        
+    def OnTimeStartSpin(self,evt):
+        self.startFrame = evt.GetInt()
+        self.timeSliderStartSpin.SetRange(self.startFrame, self.sliderEndFrame)
+        if self.startFrame >= self.sliderStartFrame:
+            self.sliderStartFrame = self.startFrame
+            self.timeSliderStartSpin.SetValue(self.sliderStartFrame)
+            self.OnTime()
+        else:
+            self.OnTime()
+    
+    def OnTimeSliderStartSpin(self,evt):
+        self.sliderStartFrame = evt.GetInt()
+        self.timeEndSpin.SetRange(self.sliderStartFrame, 10000)
+        self.OnTime()
+    
+    def OnTimeSliderEndSpin(self,evt):
+        self.sliderEndFrame = evt.GetInt()
+        self.timeStartSpin.SetRange(0, self.sliderEndFrame)
+        self.OnTime()
+    
+    def OnTimeEndSpin(self,evt):
+        self.endFrame = evt.GetInt()
+        self.timeSliderEndSpin.SetRange(self.sliderStartFrame, self.endFrame)
+        if self.endFrame <= self.sliderEndFrame:
+            self.sliderEndFrame = self.endFrame
+            self.timeSliderEndSpin.SetValue(self.sliderEndFrame)
+            self.OnTime()
+        else:
+            self.OnTime()
+    
+    def OnDeleteKey(self,evt):
+        for i in range(0,len(self.keys)):
+            if self.curFrame == self.keys[i]:
+                del self.keys[i]
+                break
+       
+        for j in self.editor.animMgr.keyFramesInfo.keys():
+            for k in range(0,len(self.editor.animMgr.keyFramesInfo[j])):
+                if self.curFrame == self.editor.animMgr.keyFramesInfo[j][k][0]: 
+                    del self.editor.animMgr.keyFramesInfo[j][k]
+                    break
+        
+        for l in self.editor.animMgr.keyFramesInfo.keys():
+            if len(self.editor.animMgr.keyFramesInfo[l]) == 0:
+                del self.editor.animMgr.keyFramesInfo[l]
+        
+        self.OnPropKey()
+        self.OnAnimation(self.curFrame)
+        
+    def OnPropKey(self):
+        self.parallel = self.editor.animMgr.createParallel(self.startFrame, self.endFrame)
+        self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame)
+       
+    
+    def OnAnimation(self, curFrame):
+        time = float(curFrame-1)/float(24)
+        self.parallel.setT(time)
+        if self.editor.GRAPH_EDITOR == True:
+            self.editor.ui.graphEditorUI.curFrameChange() 
+                  
+    def OnExit(self,evt):
+        for actor in self.editor.objectMgr.Actor:
+            actorAnim = os.path.basename(actor[OG.OBJ_ANIM])
+            actor[OG.OBJ_NP].loop(actorAnim)
+        self.parallel = None
+        self.Destroy()
+        self.editor.ui.editAnimMenuItem.Check(False)
+        self.editor.mode = self.editor.BASE_MODE
+
+
+        
+
+
+
+  

+ 53 - 0
direct/src/leveleditor/AnimGlobals.py

@@ -0,0 +1,53 @@
+"""
+This contains data structure and constants related with animation handling.
+"""
+
+# index for keyFramesInfo list structure
+# data strucrure: {[nodeUID, propertyName] : [frameNum, 
+#                                             value, 
+#                                            [inSlopeX, inSlopeY], 
+#                                            [outSlopeX, outSlopeY]]}
+UID = 0
+PROP_NAME = 1
+
+FRAME = 0
+VALUE  = 1
+INSLOPE = 2
+OUTSLOPE = 3
+
+# index for curveAnimation list structure
+# data strucrure: {[nodeUID, curveUID] : [nodeUID, 
+#                                         curveUID, 
+#                                         time]}
+NODE = 0
+CURVE = 1
+
+NODE = 0
+CURVE  = 1
+TIME = 2
+
+# index for animation curve generation information list structure(self.X, self.Y, self.Z in GraphEditorUI)
+# data structur: [key, 
+#                 i , 
+#                [[keyFrameX, keyFrameY], keyFrame_select], 
+#                [[inTangentX, inTangentY], inTangent_select], 
+#                [[outTangentX, outTangentY], outTangent_select],
+#                [inSlopeX, inSlopeY], 
+#                [outSlopeX, outSlopeY]]
+
+KEY = 0
+I = 1
+KEYFRAME = 2
+IN_TANGENT = 3
+OUT_TANGENT = 4
+IN_SLOPE = 5
+OUT_SLOPE = 6
+
+LOCAL_VALUE = 0
+SELECT = 1
+
+#index for coordinate
+X = 0
+Y = 1
+Z = 2
+

+ 10 - 0
direct/src/leveleditor/AnimMgr.py

@@ -0,0 +1,10 @@
+"""
+Defines AnimMgr
+"""
+from AnimMgrBase import *
+
+class AnimMgr(AnimMgrBase):
+    """ Animation will create, manage, update animations in the scene """
+    
+    def __init__(self, editor):
+        AnimMgrBase.__init__(self, editor)

+ 367 - 0
direct/src/leveleditor/AnimMgrBase.py

@@ -0,0 +1,367 @@
+"""
+Defines AnimMgrBase
+"""
+
+import os, wx, math
+
+from direct.interval.IntervalGlobal import *
+from panda3d.core import VBase3,VBase4
+import ObjectGlobals as OG
+import AnimGlobals as AG
+
+class AnimMgrBase:
+    """ AnimMgr will create, manage, update animations in the scene """
+    
+    def __init__(self, editor):
+        self.editor = editor
+        self.graphEditorCounter = 0
+      
+        self.keyFramesInfo = {}
+        self.curveAnimation = {}
+        
+        #normal properties
+        self.lerpFuncs={
+            'H' : self.lerpFuncH,
+            'P' : self.lerpFuncP,
+            'R' : self.lerpFuncR,
+            'SX' : self.lerpFuncSX,
+            'SY' : self.lerpFuncSY,
+            'SZ' : self.lerpFuncSZ,
+            'CR' : self.lerpFuncCR,
+            'CG' : self.lerpFuncCG,
+            'CB' : self.lerpFuncCB,
+            'CA' : self.lerpFuncCA
+            }
+        
+        #Properties which has animation curves
+        self.curveLerpFuncs={ 
+            'X' : [ self.lerpFuncX, self.lerpCurveFuncX ],
+            'Y' : [ self.lerpFuncY, self.lerpCurveFuncY ],
+            'Z' : [ self.lerpFuncZ, self.lerpCurveFuncZ ]
+            }
+            
+    def reset(self):
+        self.keyFramesInfo = {}
+        self.curveAnimation = {}
+    
+    def generateKeyFrames(self):
+        #generate keyFrame list
+        self.keyFrames = []
+        for property in self.keyFramesInfo.keys():
+            for frameInfo in self.keyFramesInfo[property]:
+                frame = frameInfo[AG.FRAME]
+                exist = False
+                for keyFrame in self.keyFrames:
+                    if frame == keyFrame:
+                        exist = True
+                        break
+                if exist == False:
+                    self.keyFrames.append(frame)  
+                    
+    def generateSlope(self, list):
+        #generate handler slope of every keyframe for animation curve
+        listLen = len(list)
+        if listLen == 2:
+            slope =[float(list[1][AG.FRAME]-list[0][AG.FRAME]),(float(list[1][AG.VALUE])-float(list[0][AG.VALUE]))]
+            list[0][AG.INSLOPE] = slope
+            list[1][AG.INSLOPE] = slope
+            list[0][AG.OUTSLOPE] = list[0][AG.INSLOPE]
+            list[1][AG.OUTSLOPE] = list[1][AG.INSLOPE]
+            return 
+        
+        if listLen >= 3:
+            list[0][AG.INSLOPE] = [float(list[1][AG.FRAME] - list[0][AG.FRAME]),(float(list[1][AG.VALUE]) - float(list[0][AG.VALUE]))]
+            list[0][AG.OUTSLOPE] = list[0][AG.INSLOPE]
+            for i in range(1, listLen-1):
+                list[i][AG.INSLOPE] = [float(list[i+1][AG.FRAME] - list[i-1][AG.FRAME]),(float(list[i+1][AG.VALUE]) - float(list[i-1][AG.VALUE]))]
+                list[i][AG.OUTSLOPE] = list[i][AG.INSLOPE]
+            list[listLen-1][AG.INSLOPE] = [float(list[listLen-1][AG.FRAME] - list[listLen-2][AG.FRAME]),(float(list[listLen-1][AG.VALUE]) - float(list[listLen-2][AG.VALUE]))]
+            list[listLen-1][AG.OUTSLOPE] = list[listLen-1][AG.INSLOPE]
+            return
+                
+    def removeAnimInfo(self, uid):
+        for property in self.keyFramesInfo.keys():
+            if property[AG.UID] == uid:
+                del self.keyFramesInfo[property]
+        self.generateKeyFrames()
+        if self.editor.mode == self.editor.ANIM_MODE:
+            self.editor.ui.animUI.OnPropKey()
+            
+    def singleCurveAnimation(self, nodePath, curve, time):
+        rope = curve[OG.OBJ_NP]
+        self.points = rope.getPoints(time)
+        self.hprs = []
+        temp = render.attachNewNode("temp")
+        temp.setHpr(0,0,0)
+        for i in range(len(self.points)-1):
+            temp.setPos(self.points[i]) 
+            temp.lookAt(self.points[i+1])
+            hpr = temp.getHpr()
+            ## self.hprs.append(hpr)
+            self.hprs.append(VBase3(hpr[0]+180,hpr[1],hpr[2]))
+        self.hprs.append(self.hprs[len(self.points)-2])
+       
+        curveSequenceName = str(nodePath[OG.OBJ_UID])+' '+str(curve[OG.OBJ_UID])+' '+str(time)
+        self.curveSequence = Sequence(name = curveSequenceName)
+        
+        for i in range(len(self.points)-1):
+            myLerp = LerpPosHprInterval(nodePath[OG.OBJ_NP], float(1)/float(24), self.points[i+1], self.hprs[i+1], self.points[i], self.hprs[i])
+            self.curveSequence.append(myLerp)
+            
+        return self.curveSequence
+
+    def createParallel(self, startFrame, endFrame):
+        self.parallel = []
+        self.parallel = Parallel(name="Current Parallel")
+        
+        self.createCurveAnimation(self.parallel) 
+        self.createActorAnimation(self.parallel, startFrame, endFrame)    
+        self.createKeyFrameAnimation(self.parallel, startFrame, endFrame)
+        self.createCurveKeyFrameAnimation(self.parallel, startFrame, endFrame)
+        
+        return self.parallel
+        
+    def createCurveAnimation(self, parallel):
+        for key in self.curveAnimation:
+            curveInfo = self.curveAnimation[key]
+            nodePath = self.editor.objectMgr.findObjectById(curveInfo[AG.NODE])
+            curve = self.editor.objectMgr.findObjectById(curveInfo[AG.CURVE])
+            time = curveInfo[AG.TIME]
+            sequence = self.singleCurveAnimation(nodePath, curve, time)
+            parallel.append(sequence)
+        
+    def createActorAnimation(self, parallel, startFrame, endFrame):
+        self.editor.objectMgr.findActors(render)
+        for actor in self.editor.objectMgr.Actor:
+            actorAnim = os.path.basename(actor[OG.OBJ_ANIM])
+            myInterval = ActorInterval(actor[OG.OBJ_NP], actorAnim, loop=1, duration = float(endFrame-startFrame+1)/float(24))
+            parallel.append(myInterval)
+        
+    def createKeyFrameAnimation(self, parallel, startFrame, endFrame):
+        #generate key frame animation for normal property
+        self.editor.objectMgr.findNodes(render)
+        for node in self.editor.objectMgr.Nodes:
+            for property in self.keyFramesInfo.keys():
+                if property[AG.UID] == node[OG.OBJ_UID] and property[AG.PROP_NAME] != 'X' and property[AG.PROP_NAME] != 'Y' and property[AG.PROP_NAME] != 'Z':
+                    mysequence = Sequence(name = node[OG.OBJ_UID])
+                    keyFramesInfo = self.keyFramesInfo[property]
+                    if len(keyFramesInfo) == 1:
+                        myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(endFrame-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                        mysequence.append(myLerp)
+                        parallel.append(mysequence)
+                    
+                    if len(keyFramesInfo) != 1:
+                        myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                        mysequence.append(myLerp)
+                        
+                        for key in range(0,len(keyFramesInfo)-1):
+                            myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[key][AG.VALUE]),toData=float(keyFramesInfo[key+1][AG.VALUE]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                            mysequence.append(myLerp)
+                           
+                        myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])    
+                        mysequence.append(myLerp)
+                        parallel.append(mysequence)
+    
+    def createCurveKeyFrameAnimation(self, parallel, startFrame, endFrame):
+        #generate key frame animation for the property which is controled by animation curve
+        self.editor.objectMgr.findNodes(render)
+        for node in self.editor.objectMgr.Nodes:
+            for property in self.keyFramesInfo.keys():
+                if property[AG.UID] == node[OG.OBJ_UID]:
+                    if property[AG.PROP_NAME] == 'X' or property[AG.PROP_NAME] == 'Y' or property[AG.PROP_NAME] == 'Z':
+                        mysequence = Sequence(name = node[OG.OBJ_UID])
+                        keyFramesInfo = self.keyFramesInfo[property]
+                        if len(keyFramesInfo) == 1:
+                            myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(endFrame-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                            mysequence.append(myLerp)
+                            parallel.append(mysequence)
+                            
+                        if len(keyFramesInfo) == 2:
+                            myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                            mysequence.append(myLerp)
+                        
+                            for key in range(0,len(keyFramesInfo)-1):
+                                self.keyFrameInfoForSingleLerp = keyFramesInfo
+                                self.keyInfoForSingleLerp = key
+                                myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[key][AG.VALUE]),toData=float(keyFramesInfo[key+1][AG.VALUE]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                                mysequence.append(myLerp)
+                           
+                            myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])    
+                            mysequence.append(myLerp)
+                            parallel.append(mysequence)
+                    
+                        if len(keyFramesInfo) > 2:
+                            myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][1]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])
+                            mysequence.append(myLerp)
+                        
+                            for key in range(0,len(keyFramesInfo)-1):
+                                myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][1],fromData=float(keyFramesInfo[key][AG.FRAME]),toData=float(keyFramesInfo[key+1][AG.FRAME]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [[node[OG.OBJ_NP], keyFramesInfo, key]])
+                                mysequence.append(myLerp)
+                           
+                            myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]])    
+                            mysequence.append(myLerp)
+                            parallel.append(mysequence)
+                            
+    def getPos(self, x, list, i):
+        #get the value from animation curve
+        x1 = float(list[i][AG.FRAME])
+        y1 = float(list[i][AG.VALUE])
+        
+        x4 = float(list[i+1][AG.FRAME])
+        y4 = float(list[i+1][AG.VALUE])
+            
+        t1x = list[i][AG.OUTSLOPE][0]
+        t1y = list[i][AG.OUTSLOPE][1]
+                
+        t2x = list[i+1][AG.INSLOPE][0]
+        t2y = list[i+1][AG.INSLOPE][1]
+        
+        x2 = x1 + (x4 - x1) / float(3)
+        scale1 = (x2 - x1) / t1x 
+        y2 = y1 + t1y * scale1 
+  
+        x3 = x4 - (x4 - x1) / float(3)
+        scale2 = (x4 - x3) / t2x 
+        y3 = y4 - t2y * scale2
+
+        ax = - float(1) * x1 + float(3) * x2 - float(3) * x3 + float(1) * x4 
+        bx =   float(3) * x1 - float(6) * x2 + float(3) * x3 + float(0) * x4  
+        cx = - float(3) * x1 + float(3) * x2 + float(0) * x3 + float(0) * x4  
+        dx =   float(1) * x1 + float(0) * x2 - float(0) * x3 + float(0) * x4  
+   
+        ay = - float(1) * y1 + float(3) * y2 - float(3) * y3 + float(1) * y4  
+        by =   float(3) * y1 - float(6) * y2 + float(3) * y3 + float(0) * y4  
+        cy = - float(3) * y1 + float(3) * y2 + float(0) * y3 + float(0) * y4  
+        dy =   float(1) * y1 + float(0) * y2 - float(0) * y3 + float(0) * y4
+
+        if ax == 0 and bx == 0 and cx == 0:
+            return 0
+            
+        if ax == 0 and bx == 0 and cx != 0:
+            a = cx
+            b = dx-x
+            t = -b/a
+            y = ay * t*t*t + by * t*t + cy * t + dy
+            return y
+            
+        if ax == 0 and bx!= 0:
+            a=bx
+            b=cx
+            c=dx-x
+            t=(-b+math.sqrt(b**2-4.0*a*c))/2*a
+            if t>=0 and t<=1:
+                y = ay * t*t*t + by * t*t + cy * t + dy
+                return y
+            else:
+                t=(-b-math.sqrt(b**2-4.0*a*c))/2*a
+                y = ay * t*t*t + by * t*t + cy * t + dy
+                return y
+        
+        if ax != 0:
+            a = ax
+            b = bx
+            c = cx
+            d = dx - float(x)
+            t = self.calculateT(a, b, c, d, x)
+            y = ay * t*t*t + by * t*t + cy * t + dy
+            return y
+        
+    def calculateT(self, a, b, c, d, x):
+        #Newton EQUATION
+        t = float(1)
+        t2 = t
+        t -= (a*t*t*t+b*t*t+c*t+d)/(float(3)*a*t*t+float(2)*b*t+c)
+        if abs(t-t2) <= 0.000001:
+            return t 
+        else:
+            while abs(t - t2) > 0.000001:
+                t2 = t
+                t -= (a*t*t*t+b*t*t+c*t+d)/(float(3)*a*t*t+float(2)*b*t+c)
+            return t 
+    
+    def lerpFuncX(self,pos,np):
+        np.setX(pos)
+        
+    def lerpFuncY(self,pos,np):
+        np.setY(pos)
+    
+    def lerpFuncZ(self,pos,np):
+        np.setZ(pos)
+            
+    def lerpCurveFuncX(self,t,extraArgs):
+        np = extraArgs[0]
+        pos = self.getPos(t, extraArgs[1], extraArgs[2])
+        np.setX(pos)
+        
+    def lerpCurveFuncY(self,t,extraArgs):
+        np = extraArgs[0]
+        pos = self.getPos(t, extraArgs[1], extraArgs[2])
+        np.setY(pos)
+    
+    def lerpCurveFuncZ(self,t,extraArgs):
+        np = extraArgs[0]
+        pos = self.getPos(t, extraArgs[1], extraArgs[2])
+        np.setZ(pos)
+    
+    def lerpFuncH(self,angle,np):
+        np.setH(angle)
+        
+    def lerpFuncP(self,angle,np):
+        np.setP(angle)
+        
+    def lerpFuncR(self,angle,np):
+        np.setR(angle)
+        
+    def lerpFuncSX(self,scale,np):
+        np.setSx(scale)
+        
+    def lerpFuncSY(self,scale,np):
+        np.setSy(scale)
+        
+    def lerpFuncSZ(self,scale,np):
+        np.setSz(scale)
+        
+    def lerpFuncCR(self,R,np):
+        obj = self.editor.objectMgr.findObjectByNodePath(np)
+        r = obj[OG.OBJ_RGBA][0]
+        g = obj[OG.OBJ_RGBA][1]
+        b = obj[OG.OBJ_RGBA][2]
+        a = obj[OG.OBJ_RGBA][3]
+        self.colorUpdate(R,g,b,a,np)
+        
+    def lerpFuncCG(self,G,np):
+        obj = self.editor.objectMgr.findObjectByNodePath(np)
+        r = obj[OG.OBJ_RGBA][0]
+        g = obj[OG.OBJ_RGBA][1]
+        b = obj[OG.OBJ_RGBA][2]
+        a = obj[OG.OBJ_RGBA][3]
+        self.colorUpdate(r,G,b,a,np)
+        
+    def lerpFuncCB(self,B,np):
+        obj = self.editor.objectMgr.findObjectByNodePath(np)
+        r = obj[OG.OBJ_RGBA][0]
+        g = obj[OG.OBJ_RGBA][1]
+        b = obj[OG.OBJ_RGBA][2]
+        a = obj[OG.OBJ_RGBA][3]
+        self.colorUpdate(r,g,B,a,np)
+    
+    def lerpFuncCA(self,A,np):
+        obj = self.editor.objectMgr.findObjectByNodePath(np)
+        r = obj[OG.OBJ_RGBA][0]
+        g = obj[OG.OBJ_RGBA][1]
+        b = obj[OG.OBJ_RGBA][2]
+        a = obj[OG.OBJ_RGBA][3]
+        self.colorUpdate(r,g,b,A,np)
+    
+    def colorUpdate(self, r, g, b, a, np):
+        if base.direct.selected.last == None:
+            self.editor.objectMgr.updateObjectColor(r, g, b, a, np)
+        elif self.editor.objectMgr.findObjectByNodePath(np) == self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last):
+            self.editor.ui.objectPropertyUI.propCR.setValue(r)
+            self.editor.ui.objectPropertyUI.propCG.setValue(g)
+            self.editor.ui.objectPropertyUI.propCB.setValue(b)
+            self.editor.ui.objectPropertyUI.propCA.setValue(a)        
+            self.editor.objectMgr.updateObjectColor(r, g, b, a, np)
+        else:
+            self.editor.objectMgr.updateObjectColor(r, g, b, a, np)
+        

+ 159 - 0
direct/src/leveleditor/CurveAnimUI.py

@@ -0,0 +1,159 @@
+"""
+   This is the GUI for the Curve Animation
+"""
+import os, wx, time
+
+from direct.interval.IntervalGlobal import *
+from direct.actor.Actor import *
+from direct.showutil.Rope import Rope
+import ObjectGlobals as OG
+
+class CurveAnimUI(wx.Dialog):
+    """
+    This is the Curve Animation Panel implementation.
+    """
+    def __init__(self, parent, editor):
+        wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Curve Animation",
+                           pos=wx.DefaultPosition, size=(430, 140))
+        
+        self.editor = editor
+        self.nodePath = None
+        self.curve = None
+                           
+        self.mainPanel = wx.Panel(self, -1)
+        
+        self.chooseNode = wx.StaticText( self.mainPanel, -1, "Choose NodePath:")
+        self.chooseNodeTxt = wx.TextCtrl( self.mainPanel, -1, "")
+        self.chooseNodeButton = wx.Button( self.mainPanel, -1, "Choose..")
+        
+        self.chooseCurve = wx.StaticText( self.mainPanel, -1, "Choose attch Curve:")
+        self.chooseCurveTxt = wx.TextCtrl( self.mainPanel, -1, "")
+        self.chooseCurveButton = wx.Button( self.mainPanel, -1, "Choose..")
+        
+        self.duritionTime = wx.StaticText( self.mainPanel, -1, "Durition(Frame):")
+        self.duritionTimeSpin = wx.SpinCtrl( self.mainPanel, -1, "",size = (70,25), min=24, max=10000)
+        
+        self.createAnimButton = wx.Button( self.mainPanel, -1, "Creat")
+        self.saveAnimButton = wx.Button( self.mainPanel, -1, "Save Animation")
+        
+        self.SetProperties()
+        self.DoLayout()
+        
+        self.Bind(wx.EVT_BUTTON, self.OnChooseNode, self.chooseNodeButton)
+        self.Bind(wx.EVT_BUTTON, self.OnChooseCurve, self.chooseCurveButton)
+        self.Bind(wx.EVT_BUTTON, self.OnCreateAnim, self.createAnimButton)
+        self.Bind(wx.EVT_BUTTON, self.OnSaveAnim, self.saveAnimButton)
+
+        self.Bind(wx.EVT_CLOSE, self.OnExit)
+        
+    def SetProperties(self):
+        self.duritionTimeSpin.SetValue(24)
+        self.chooseNodeTxt.SetMinSize((200,21))
+        self.chooseCurveTxt.SetMinSize((200,21))
+        self.saveAnimButton.SetToolTipString("Save the animation to the global animation control")
+    
+    def DoLayout(self):
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer = wx.FlexGridSizer(4, 3, 0, 0)
+        
+        mainSizer.Add(self.chooseNode, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10)
+        mainSizer.Add(self.chooseNodeTxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer.Add(self.chooseNodeButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        
+        mainSizer.Add(self.chooseCurve, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10)
+        mainSizer.Add(self.chooseCurveTxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer.Add(self.chooseCurveButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        
+        mainSizer.Add(self.duritionTime, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10)
+        mainSizer.Add(self.duritionTimeSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        mainSizer.Add(self.createAnimButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        
+        mainSizer.Add(self.saveAnimButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
+        
+        self.mainPanel.SetSizerAndFit(mainSizer)
+        
+        dialogSizer.Add(self.mainPanel, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
+        
+        self.SetSizer(dialogSizer)
+        self.Layout()
+        
+    def OnChooseNode(self, evt):
+        if base.direct.selected.last == None or base.direct.selected.last.hasTag('Controller') or not base.direct.selected.last.hasTag('OBJRoot'):
+            dlg = wx.MessageDialog(None, 'Please select an object.', 'NOTICE', wx.OK )
+            dlg.ShowModal()
+            dlg.Destroy()
+        else:
+            obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
+            if obj[OG.OBJ_DEF].name == '__Curve__':
+                dlg = wx.MessageDialog(None, 'Please select an object, not a curve.', 'NOTICE', wx.OK )
+                dlg.ShowModal()
+                dlg.Destroy()
+            else:
+                self.nodePath = obj
+                self.chooseNodeTxt.SetValue(str(self.nodePath[OG.OBJ_UID]))
+    
+    def OnChooseCurve(self, evt):
+        if base.direct.selected.last == None or base.direct.selected.last.hasTag('Controller') or not base.direct.selected.last.hasTag('OBJRoot'):
+            dlg = wx.MessageDialog(None, 'Please select a curve.', 'NOTICE', wx.OK )
+            dlg.ShowModal()
+            dlg.Destroy()
+        else:
+            obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
+            if obj[OG.OBJ_DEF].name != '__Curve__':
+                dlg = wx.MessageDialog(None, 'Please select a curve, not an object.', 'NOTICE', wx.OK )
+                dlg.ShowModal()
+                dlg.Destroy()
+            elif obj[OG.OBJ_DEF].name == '__Curve__':
+                self.curve = obj
+                self.chooseCurveTxt.SetValue(str(self.curve[OG.OBJ_UID]))
+        
+    def OnCreateAnim(self, evt):
+        self.time = self.duritionTimeSpin.GetValue()
+        if self.nodePath == None or self.curve == None:
+            dlg = wx.MessageDialog(None, 'Please select an object and a curve first.', 'NOTICE', wx.OK )
+            dlg.ShowModal()
+            dlg.Destroy()
+        else:
+            self.curveSequence = self.editor.animMgr.singleCurveAnimation(self.nodePath, self.curve, self.time)
+            self.curveSequence.start()
+   
+    def OnSaveAnim(self,evt):
+        if not self.curveSequence:
+            dlg = wx.MessageDialog(None, 'Please create a animation first.', 'NOTICE', wx.OK )
+            dlg.ShowModal()
+            dlg.Destroy()
+        else:
+            if self.editor.animMgr.curveAnimation == {}:
+                self.editor.animMgr.curveAnimation[(self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID])] = (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID],self.time)
+                self.editor.updateStatusReadout('Sucessfully saved to global animation list')
+                return 
+            
+            hasKey = False
+            for key in self.editor.animMgr.curveAnimation.keys():
+                if key == (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID]):
+                    dlg = wx.MessageDialog(None, 'Already have the animation for this object attach to this curve.', 'NOTICE', wx.OK )
+                    dlg.ShowModal()
+                    dlg.Destroy()
+                    hasKey = True
+                    return
+                elif self.nodePath[OG.OBJ_UID] == key[0]:
+                    dlg = wx.MessageDialog(None, 'This object is already attached to a curve.', 'NOTICE', wx.OK )
+                    dlg.ShowModal()
+                    dlg.Destroy()
+                    hasKey = True
+                    return
+            
+            if hasKey == False and self.editor.animMgr.curveAnimation != {}:
+                self.editor.animMgr.curveAnimation[(self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID])] = (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID],self.time)
+                self.editor.updateStatusReadout('Sucessfully saved to global animation list')
+        
+    def OnExit(self,evt):
+        self.Destroy()
+        self.editor.ui.curveAnimMenuItem.Check(False)
+
+
+        
+
+
+
+  

+ 3 - 0
direct/src/leveleditor/FileMgr.py

@@ -19,12 +19,15 @@ class FileMgr:
             f.write("from pandac.PandaModules import *\n")
             f.write("\nif hasattr(base, 'le'):\n")
             f.write("    objectMgr = base.le.objectMgr\n")
+            f.write("    animMgr = base.le.animMgr\n")
             f.write("    ui = base.le.ui\n")
             f.write("    ui.sceneGraphUI.reset()\n\n")
             f.write("else:\n")
             f.write("    objectMgr = base.objectMgr\n")
             f.write("# temporary place holder for nodepath\n")
             f.write("objects = {}\n")
+            f.write("animMgr.keyFramesInfo = "+str(self.editor.animMgr.keyFramesInfo)+"\n")
+            f.write("animMgr.curveAnimation = "+str(self.editor.animMgr.curveAnimation)+"\n")
             saveData = self.editor.objectMgr.getSaveData()
             for data in saveData:
                 f.write(data)

+ 918 - 0
direct/src/leveleditor/GraphEditorUI.py

@@ -0,0 +1,918 @@
+"""
+Defines Graph Editor
+"""
+import os,wx
+import math
+import cPickle as pickle
+from PaletteTreeCtrl import *
+import ObjectGlobals as OG
+import AnimGlobals as AG
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+property =  [
+    "translateX",
+    "translateY",
+    "translateZ"
+    ]
+
+#----------------------------------------------------------------------
+ZoomIn = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACgklE"
+    "QVRIid3VW0vjQBQA4DlJMziZlCKCSB/9Bf4a3wRtNU6SHyXK6pay2nVbQbasskXFO15SqmwL"
+    "LtYnn7Zg20zSyT50wZikXsCHxXk9Z74ZZuacgebtLXrvIb27+PFQjDEADIoqiiLL8htQAMAY"
+    "Hx4dPTw8SHEuIaRWqzWbzUQi8SoUAIaGhgqFgn15SSn1Iwmqqh4fH5c2NpLJpO8/xhORzEeR"
+    "EJLL5VqtViaT8TxPCBFMoJTu7e1tb28zxpLJpOu6L6D9PS6vrHQ6nWw267puVNzZ2alUKqZh"
+    "UE0LivFoX/y0vOxynpmZ4ZxHxZ+Vyu7urmmaqqqGxBhUkiSM8eLiIgDMTE87EVHTtB9bW4cH"
+    "B5ZpEkKiYhjt3/XCwoKiKFNTU47jRPf4vVw+PTmxLAtjHCs+QQEAAJaWljRNm5yc7Ha7wQtF"
+    "CGGMy+WybduWZSmK4nlerIiCTwoAOOfX19cTExMSgPDDT0iSJNu2x8fHU6nUM+ITVAhBCLFM"
+    "83MuV63VKCGhVM65ruv1RqNYLFJKX4UihFzXTafTc7Oz+XzerlZVVQ1GhRCyLJuGcXV1VSqV"
+    "QtGBKEKo6zhjY2Pzur66tnZxcRGa2ev1AMA0zXqj8W3wfmPK1HGc0dFRNj//dX397Ows6iKE"
+    "DMZ+39wUCgVKabTXxNe+4zgjIyMGY8VS6eT0NOr6vs8Yu7u7+7K6SggJuQMbCud8eHjYYGxz"
+    "c3N/f19RlJDb6/V0Xb+/v8/n829ofZzzVCqlz82dn5//abUk6UmyEEIIkc1k2u32r3o9uCq8"
+    "+PHJsuz7vud5IfTffABZljnnwejLnb9/grEiQih2vVd9J36kup4fH+w3/S/QvwejQg8ibHgo"
+    "AAAAAElFTkSuQmCC")
+    
+#----------------------------------------------------------------------
+ZoomOut = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACmUlE"
+    "QVRIieXVz0sbQRQH8NmdH8smh3jwEIIlB+tNEW1NVps0WxPBv0BQXKQx4N2/xR2xTYQ9iIHc"
+    "/A+shyqevHiQVBGLeqgoxM1kNjHTQ8DayWb14KX0Hfe9+fDd4bGr/Ly4AK9d6quL/y6qKIqm"
+    "aQHThBBVfT7HnwlFUTjn3/f3MUKKoviMqurh4WG9XkcIvRQVQoRCodPT0+1yWdO0bhchxD2v"
+    "WCxyzjHGL0IBAA8PD9biIgDAcRxN06Q39Tzvk2kahmHbNmMswP3rmBCi0WhYlkUI2dzcxBhL"
+    "ruu6mUwmnU6v2bbrur1c+dY77sLCQjgcLpVKGKFuN5VKmaZpU1qr1Qghz6OP7vz8fF9f39di"
+    "Efq5H6amcrkcpfTu7q7b9d8PIQRjbG5urr+//8vGBoSw200mErOzs3R9/fb2VnLh6uqqrwsA"
+    "aLVao6OjV9fX3/b2RkZGJLfZbMbj8UgkUqlU3g4NhcNhIURQ0k612+12u/1ufPxHtcoY6157"
+    "z/OGh4fv7++vLi8hhI/Pg9ZY1/Xz83PHcVZWViKRSLPZfNpFCHmeRynNZrNjY2ONRuOx1TOp"
+    "rutnZ2eO41iWNTg4KIkYY875mm2/n5iYnp5+KvZMqut6tVrd2tpaWlqKx+OMMUmsM0YpnZqc"
+    "NE3TdV3puA+q6/rJycl2ufw5n38zMCClwBjXXdem9GMmk06lukUfNBQKHR8fVyqVwvJyLBaT"
+    "REJIrVbr3KNhGL6ijGKMj46OdnZ2CoVCNBqVRAjhr5ubUqmUm5kxksleIpA+fYyxg4ODfD4f"
+    "jUY559IohHB3dzebzSYTiQARAKA8/fEJIVRVhRC2Wi3faSEEIUTahKCknbAAgF4iAEBV1WdF"
+    "Ge1kCZgO7vZEX6X+c/Q3Qy8zmmCLWqIAAAAASUVORK5CYII=")
+    
+#----------------------------------------------------------------------
+OneTangent = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACKUlE"
+    "QVRIia2Wz2saURDH5/3Yt6aVsE3SomArGO0phxCSU6A2EFgSklP+qPwrXkOQkBCoaG8eQrBQ"
+    "KRYVU1tabUij3V1337weAtZI1FX3e9qF2c/OzJv9zpKbRgMGRAhRSsF8oo9uOCeUMk0LDEo1"
+    "rVUuF46Pv2SzhDEgJBhoLZer5/OViwu32yWUjnlsvHj/SrluPJ222m3GORMC5ugsGTwoyjkT"
+    "4nuxeFetvj06ko4zG/RRjeh5rmVFNjeZrt8UCnxhIQAoAIBSnm0n9vb+1Gq/y2Wm60FAAUAp"
+    "UGr18LCRz9vtNuH8iZipoQAopW4Yb3Z2vmazgDjtJIyMlo7zIpUyksnq2RkTIhgoAHiWFdve"
+    "RsTmx4IuNP/ZToiUvV7CNNt3natb61b57cKEMIXIOeu+N0uof5IaEl8f7+R3K1SG8sKc0FqF"
+    "oPTjCZOhEiDF0Vyk8da3+occC4UCgAKAAhBe73X6Xa/T+VEs8klcv0eKCtD1kgcHP6+v7xsN"
+    "OtZzp5hqhUh1PbG/Xz0/l5ZFGAsACgDous+j0cjWVuX0lI428qmdWNr2q/X10NJS/fJyVHNn"
+    "sXfPtuO7u39brV+lkgiHmRBDKZOhbepThDHpOJ8zmZ7jLMZiq6YJhPSXxYyLSEkZMgx9ebl8"
+    "clLKZO6bTTbgkFN7ZV+e47xcW4tubIQjkWcrKyjl/zpmK/9BlDH0vIfZUogBZAoAKCWhVCk1"
+    "tHrnggLAk/9I/wC0eekA1bLbIQAAAABJRU5ErkJggg==")
+    
+#----------------------------------------------------------------------
+TwoTangents = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACL0lE"
+    "QVRIie3UzU8TQRQA8DczO1vatRgh1YhWD/WD+kG91FgOXLhx4+CpiYnE9L8g/gVwJHhp4lUP"
+    "pPHgQRMOGiEaDabV1LRAtdjUAG23m3W/Znc8YEo/trRNOGjCO+7M++3MezODdopFOO7Axy6e"
+    "oC0oAqAEY4QGyCcE4faVtaII7dQUy7b7dDEheq3GdB0R4o76KH2+kZ158uzxyzcO56iXS0Rx"
+    "N5tdnZ9fW1w0FaXZPUQxRl/Le7+q8mruh2paQsem2kLweCq5XGVrq7K5adTruGk+ahx+AeOS"
+    "rLz4ktctdtYvPYje0i3Gu22cUr1a3VheHg6FhoPBC9EoBwDO21EOQDH2igIALL39NOrz3r8z"
+    "rppWp3jQmQ8LC5enp4NTU0zTmGG0/PJwKgBzHEU3VcN6eHfie7X++ltBEmknSoaG0snkSDg8"
+    "Njmp12ptIrieU4dzAEjEIu8KPz8Wy14qNI9SScqvrHCA67Ozlqq6F8f1K3McL6WPYpFUOre9"
+    "L3uEv50VfL7S+vpuJjMxN8cMo1HEvlAAMG37nF+KR28+fZ/e/62JBAuiWC8U8qlUJJFAhHDH"
+    "6ZZ71LnRLXY1MDJzI5Rc+6wwR5PlTDIZjsd9gYBjuTSwEajneypR4dV2qXxq1F/dG5fLodg9"
+    "U9OOThGOHgYAg7FrF8fKKqjng/4rl+xeYl+ozeEMsNsiwmCftnnXQg6EcgABICxyAGAcut2x"
+    "wdADt+uFdYv/9OU/Qf9d9A9dhOgRU8MmewAAAABJRU5ErkJggg==")
+    
+#----------------------------------------------------------------------
+
+class GraphEditorWindow(wx.Window):
+    """
+    This is the main graph editor window.
+    """
+    def __init__(self, parent, windowSize, property, xRange, yRange, curFrame, object):
+        wx.Window.__init__(self, parent, size = windowSize, style = wx.SUNKEN_BORDER)
+    
+        self._mainDialog = wx.GetTopLevelParent(self)
+        self.w,self.h = self.GetClientSize()
+        
+        self.zoom = float(2)
+        self._mouseIn = False
+        self._selectRec = False
+        self._selectHandler = False
+        self._OneTangent = True
+        
+        self.object = object
+        self.curFrame = curFrame
+        self.property = property
+      
+        self.zeroPos = (float(0), self.h/float(2))
+        self.zero = 0
+        self.unitWidth = self.w/float(xRange)
+        self.unitHeight = self.h/float(yRange)
+        
+        self.generateInfo()
+        self.InitBuffer()
+        
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
+        self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+        
+    def refresh(self):
+        self._mouseIn = False
+        self._selectRec = False
+        self._selectHandler = False
+        self.generateInfo()
+    
+    def generateInfo(self):
+        #generate the information for animation curve generation
+        self.X = []
+        self.Y = []
+        self.Z = []
+        
+        if self._mainDialog.editor.animMgr.keyFramesInfo != {}:
+            self.keyFramesInfo = self._mainDialog.editor.animMgr.keyFramesInfo
+            for key in self.keyFramesInfo.keys():
+                if key == (self.object[OG.OBJ_UID], 'X'):
+                    for i in range(len(self.keyFramesInfo[key])):
+                        item = self.keyFramesInfo[key][i]
+                        handler = self.generateHandler(item)
+                        self.X.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]])
+                if key == (self.object[OG.OBJ_UID], 'Y'):
+                    for i in range(len(self.keyFramesInfo[key])):
+                        item = self.keyFramesInfo[key][i]
+                        handler = self.generateHandler(item)
+                        self.Y.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]])
+                if key == (self.object[OG.OBJ_UID], 'Z'):
+                    for i in range(len(self.keyFramesInfo[key])):
+                        item = self.keyFramesInfo[key][i]
+                        handler = self.generateHandler(item)
+                        self.Z.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]])
+    
+    def generateHandler(self, item):
+        #generate the position for the control handler
+        x1 = self.zeroPos[0] + float(item[AG.FRAME])*self.unitWidth 
+        y1 = self.zeroPos[1] - float(item[AG.VALUE])*self.unitHeight
+            
+        t1x = item[AG.INSLOPE][0]*self.unitWidth
+        t1y = item[AG.INSLOPE][1]*self.unitHeight
+                
+        t2x = item[AG.OUTSLOPE][0]*self.unitWidth 
+        t2y = item[AG.OUTSLOPE][1]*self.unitHeight
+                
+        tanA = t1y/t1x
+        temp1 = float(1)/(tanA**2+1)
+        if t1x <0 :
+            cosA = -math.sqrt(abs(temp1))
+        if t1x >=0:
+            cosA = math.sqrt(abs(temp1))
+        temp2 = (tanA**2)*temp1
+        if t1y <0 :
+            sinA = -math.sqrt(abs(temp2))
+        if t1y >=0:
+            sinA = math.sqrt(abs(temp2))
+                
+        x2 = x1-float(self.unitWidth*self.zoom)*cosA
+        y2 = y1+float(self.unitWidth*self.zoom)*sinA
+       
+        tanA = t2y/t2x
+        temp1 = float(1)/(tanA**2+1)
+        if t2x <0 :
+            cosA = -math.sqrt(abs(temp1))
+        if t2x >=0:
+            cosA = math.sqrt(abs(temp1))
+        temp2 = (tanA**2)*temp1
+        if t2y <0 :
+            sinA = -math.sqrt(abs(temp2))
+        if t2y >=0:
+            sinA = math.sqrt(abs(temp2))
+            
+        x3 = x1+float(self.unitWidth*self.zoom)*cosA
+        y3 = y1-float(self.unitWidth*self.zoom)*sinA
+    
+        return [[(x1,y1),0],[(x2,y2),0],[(x3,y3),0],[t1x,t1y],[t2x,t2y]]
+        
+    def InitBuffer(self):
+        self.buffer = wx.EmptyBitmap(self.w, self.h)
+        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+        self.DrawXCoord(dc)
+        self.DrawYCoord(dc)
+        self.DrawFrame(dc)
+        self.DrawCurve(dc)
+        self.DrawSelectRec(dc)
+        
+    def SetGraphEditorData(self, property, curFrame = 1):
+        self.curFrame = curFrame
+        self.property = property
+        
+        self.InitBuffer()
+        
+    def OnPaint(self, evt):
+        dc = wx.BufferedPaintDC(self, self.buffer)
+   
+    def DrawXCoord(self,dc):
+        dc.SetBackground(wx.Brush(wx.Colour(200, 200, 200)))
+        dc.Clear()
+        
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
+        
+        dc.DrawLine(self.zeroPos[0], float(0), self.zeroPos[0], self.h) 
+        st = str(self.zero)
+        self.tw,self.th = dc.GetTextExtent(st)  
+        dc.DrawText(st, self.zeroPos[0]+1.0, self.h-self.th-0.5)
+        
+        dc.SetPen(wx.Pen(wx.Colour(150, 150, 150)))
+        dc.SetBrush(wx.Brush(wx.Colour(150, 150, 150)))
+        
+        if self.unitWidth >= 25:
+            posPos = self.zeroPos[0]+self.unitWidth
+            posNum = self.zero + 1
+            while posPos <= self.w:
+                dc.DrawLine(posPos, float(0), posPos, self.h)
+                st = str(posNum)
+                self.drawXNumber(dc, st, posPos)
+                posPos += self.unitWidth
+                posNum += 1
+            
+            negPos = self.zeroPos[0]-self.unitWidth
+            negNum = self.zero - 1
+            while negPos >= float(0):
+                dc.DrawLine(negPos, float(0), negPos, self.h)
+                st = str(negNum)
+                self.drawXNumber(dc, st, negPos)
+                negPos -= self.unitWidth  
+                posNum -= 1
+        
+        elif self.unitWidth >= 10 and self.unitWidth <= 25:
+            posPos = self.zeroPos[0]+self.unitWidth*float(2)
+            posNum = self.zero + 2
+            while posPos <= self.w:
+                dc.DrawLine(posPos, float(0), posPos, self.h)
+                st = str(posNum)
+                self.drawXNumber(dc, st, posPos)
+                posPos += self.unitWidth*float(2) 
+                posNum += 2
+            
+            negPos = self.zeroPos[0]-self.unitWidth*float(2)
+            negNum = self.zero - 2
+            while negPos >= float(0):
+                dc.DrawLine(negPos, float(0), negPos, self.h)
+                st = str(negNum)
+                self.drawXNumber(dc, st, negPos)
+                negPos -= self.unitWidth*float(2)  
+                posNum -= 2
+                
+        elif self.unitWidth >= 2 and self.unitWidth <= 10:
+            posPos = self.zeroPos[0]+self.unitWidth*float(5)
+            posNum = self.zero + 5
+            while posPos <= self.w:
+                dc.DrawLine(posPos, float(0), posPos, self.h)
+                st = str(posNum)
+                self.drawXNumber(dc, st, posPos)
+                posPos += self.unitWidth*float(5) 
+                posNum += 5
+            
+            negPos = self.zeroPos[0]-self.unitWidth*float(5)
+            negNum = self.zero - 5
+            while negPos >= float(0):
+                dc.DrawLine(negPos, float(0), negPos, self.h)
+                st = str(negNum)
+                self.drawXNumber(dc, st, negPos)
+                negPos -= self.unitWidth*float(5)  
+                posNum -= 5
+            
+    def DrawYCoord(self,dc):
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
+       
+        dc.DrawLine(float(0), self.zeroPos[1], self.w, self.zeroPos[1]) 
+        st = str(self.zero)
+        dc.DrawText(st, 5.0, self.zeroPos[1]-1.0)
+        
+        dc.SetPen(wx.Pen(wx.Colour(150, 150, 150)))
+        dc.SetBrush(wx.Brush(wx.Colour(150, 150, 150)))
+        dc.SetLogicalFunction(wx.AND)
+        
+        posPos = self.zeroPos[1]-self.unitHeight*float(5)
+        posNum = self.zero + 5
+        while posPos >= float(0):
+            dc.DrawLine(float(0), posPos, self.w, posPos)
+            st = str(posNum)
+            self.drawYNumber(dc, st, posPos)
+            posPos -= self.unitHeight*float(5)
+            posNum += 5
+            
+        negPos = self.zeroPos[1]+self.unitHeight*float(5)
+        negNum = self.zero - 5
+        while negPos <= self.h:
+            dc.DrawLine(float(0), negPos, self.w, negPos)
+            st = str(negNum)
+            self.drawYNumber(dc, st, negPos)
+            negPos += self.unitHeight*float(5)  
+            negNum -= 5
+            
+    def drawXNumber(self, dc, st, pos):
+        oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
+                
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.DrawText(st, pos+1.0, self.h-self.th-0.5)
+                
+        dc.SetPen(oldPen)
+        dc.SetBrush(oldBrush)
+        dc.SetLogicalFunction(oldMode)
+            
+    def drawYNumber(self, dc, st, pos):
+        oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
+                
+        dc.SetPen(wx.BLACK_PEN)
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.DrawText(st, 5.0, pos-1.0)
+                
+        dc.SetPen(oldPen)
+        dc.SetBrush(oldBrush)
+        dc.SetLogicalFunction(oldMode)
+            
+    def DrawFrame(self, dc):
+        if self._mainDialog.editor.mode == self._mainDialog.editor.ANIM_MODE:
+            curFramePos = self.zeroPos[0]+self.curFrame*self.unitWidth
+            dc.SetPen(wx.Pen("red"))
+            dc.SetBrush(wx.Brush("red"))
+            dc.DrawLine(curFramePos, float(0), curFramePos, self.h)
+        else:
+            pass
+   
+    def drawX(self, dc):
+        dc.SetPen(wx.Pen("red"))
+        dc.SetBrush(wx.Brush("red"))
+        self.drawSingleCurve(self.X, dc)
+        self.drawKeys(self.X, dc)
+        self.drawHandler(self.X, dc)
+    
+    def drawY(self, dc):
+        dc.SetPen(wx.Pen("green"))
+        dc.SetBrush(wx.Brush("green"))
+        self.drawSingleCurve(self.Y, dc)
+        self.drawKeys(self.Y, dc)
+        self.drawHandler(self.Y, dc)
+    
+    def drawZ(self, dc):
+        dc.SetPen(wx.Pen("blue"))
+        dc.SetBrush(wx.Brush("blue"))
+        self.drawSingleCurve(self.Z, dc)
+        self.drawKeys(self.Z, dc)
+        self.drawHandler(self.Z, dc)
+        
+    def DrawCurve(self, dc):
+        if self.property == self._mainDialog.namestr:
+           self.drawX(dc)
+           self.drawY(dc)
+           self.drawZ(dc)
+           return
+        if self.property == property[AG.X]:
+           self.drawX(dc)
+           return
+        if self.property == property[AG.Y]:
+           self.drawY(dc)
+           return
+        if self.property == property[AG.Z]:
+           self.drawZ(dc) 
+           return 
+    
+    def drawSingleCurve(self, list, dc):
+        if len(list) == 1:
+            dc.DrawPoint(list[0][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[0][AG.KEYFRAME][AG.LOCAL_VALUE][1])
+            return
+            
+        if len(list) == 2:
+            dc.DrawLine(list[0][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[0][AG.KEYFRAME][AG.LOCAL_VALUE][1], list[1][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[1][AG.KEYFRAME][AG.LOCAL_VALUE][1])
+            return
+            
+        if len(list)>=3 :
+            for i in range(len(list)-1):
+                x1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]
+                y1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]
+            
+                x4 = list[i+1][AG.KEYFRAME][AG.LOCAL_VALUE][0]
+                y4 = list[i+1][AG.KEYFRAME][AG.LOCAL_VALUE][1]
+            
+                t1x = list[i][AG.OUT_SLOPE][0]
+                t1y = list[i][AG.OUT_SLOPE][1]
+                
+                t2x = list[i+1][AG.IN_SLOPE][0]
+                t2y = list[i+1][AG.IN_SLOPE][1]
+ 
+                x2 = x1 + (x4 - x1) / float(3);  
+                scale1 = (x2 - x1) / t1x;  
+                y2 = y1 - t1y * scale1;  
+   
+  
+                x3 = x4 - (x4 - x1) / float(3);  
+                scale2 = (x4 - x3) / t2x;  
+                y3 = y4 + t2y * scale2; 
+                
+                ax = - float(1) * x1 + float(3) * x2 - float(3) * x3 + float(1) * x4;  
+                bx =   float(3) * x1 - float(6) * x2 + float(3) * x3 + float(0) * x4;  
+                cx = - float(3) * x1 + float(3) * x2 + float(0) * x3 + float(0) * x4;  
+                dx =   float(1) * x1 + float(0) * x2 - float(0) * x3 + float(0) * x4;  
+   
+                ay = - float(1) * y1 + float(3) * y2 - float(3) * y3 + float(1) * y4;  
+                by =   float(3) * y1 - float(6) * y2 + float(3) * y3 + float(0) * y4;  
+                cy = - float(3) * y1 + float(3) * y2 + float(0) * y3 + float(0) * y4;  
+                dy =   float(1) * y1 + float(0) * y2 - float(0) * y3 + float(0) * y4;  
+
+                preX = x1 
+                preY = y1 
+                t = 0.001
+   
+                while t<=float(1):
+                    x = ax * t*t*t + bx * t*t + cx * t + dx;  
+                    y = ay * t*t*t + by * t*t + cy * t + dy;
+
+                    curX = x 
+                    curY = y
+                    
+                    dc.DrawLine(preX, preY, curX, curY)
+                    
+                    preX = curX
+                    preY = curY
+                    
+                    t += 0.001  
+
+    def drawKeys(self, list, dc):
+        for i in range(len(list)):
+            pointX = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]
+            pointY = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]
+            
+            if list[i][AG.KEYFRAME][AG.SELECT] == 0:
+                dc.SetPen(wx.Pen("black",3))
+                dc.SetBrush(wx.Brush("black"))
+                dc.DrawCircle(pointX, pointY, 2)
+            
+            if list[i][AG.KEYFRAME][AG.SELECT] == 1:
+                dc.SetPen(wx.Pen("cyan",3))
+                dc.SetBrush(wx.Brush("cyan"))
+                dc.DrawCircle(pointX, pointY, 2)
+    
+    def drawHandler(self, list, dc):
+        for i in range(len(list)):
+            if list[i][AG.KEYFRAME][AG.SELECT] == 1:
+                X1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]
+                Y1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] 
+                if self._OneTangent == True:
+                    for j in range(3,5):
+                        X = list[i][j][AG.LOCAL_VALUE][0]
+                        Y = list[i][j][AG.LOCAL_VALUE][1]
+                        if list[i][j][AG.SELECT] == 1:
+                            dc.SetPen(wx.Pen("cyan",3))
+                            dc.SetBrush(wx.Brush("cyan"))
+                            dc.DrawCircle(X, Y, 2)
+                        
+                            dc.SetPen(wx.Pen("cyan",1))
+                            dc.DrawLine(X1, Y1, X, Y)
+                    
+                        if list[i][j][AG.SELECT] == 0:
+                            dc.SetPen(wx.Pen("brown",3))
+                            dc.SetBrush(wx.Brush("brown"))
+                            dc.DrawCircle(X, Y, 2)
+                        
+                            dc.SetPen(wx.Pen("brown",1))
+                            dc.DrawLine(X1, Y1, X, Y)
+                            
+                if self._OneTangent == False:
+                    if list[i][AG.IN_TANGENT][AG.SELECT] == 1:
+                        X = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0]
+                        Y = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1]
+                        dc.SetPen(wx.Pen("cyan",3))
+                        dc.SetBrush(wx.Brush("cyan"))
+                        dc.DrawCircle(X, Y, 2)
+
+                        dc.SetPen(wx.Pen("cyan",1))
+                        dc.DrawLine(X1, Y1, X, Y)
+                    
+                    if list[i][AG.IN_TANGENT][AG.SELECT] == 0:
+                        X = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0]
+                        Y = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1]
+                        dc.SetPen(wx.Pen("navy",3))
+                        dc.SetBrush(wx.Brush("navy"))
+                        dc.DrawCircle(X, Y, 2)
+                        
+                        dc.SetPen(wx.Pen("navy",1))
+                        dc.DrawLine(X1, Y1, X, Y)
+                        
+                    if list[i][AG.OUT_TANGENT][AG.SELECT] == 1:
+                        X = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0]
+                        Y = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1]
+                        dc.SetPen(wx.Pen("cyan",3))
+                        dc.SetBrush(wx.Brush("cyan"))
+                        dc.DrawCircle(X, Y, 2)
+
+                        dc.SetPen(wx.Pen("cyan",1))
+                        dc.DrawLine(X1, Y1, X, Y)
+                    
+                    if list[i][AG.OUT_TANGENT][AG.SELECT] == 0:
+                        X = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0]
+                        Y = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1]
+                        dc.SetPen(wx.Pen("brown",3))
+                        dc.SetBrush(wx.Brush("brown"))
+                        dc.DrawCircle(X, Y, 2)
+
+                        dc.SetPen(wx.Pen("brown",1))
+                        dc.DrawLine(X1, Y1, X, Y)
+
+    def DrawSelectRec(self, dc):
+        if self._selectRec == True:
+            dc.SetPen(wx.Pen("navy", 1))
+            dc.SetBrush(wx.Brush("navy"))
+            ## dc.SetLogicalFunction(wx.AND)
+            dc.DrawLine(self.pos[0], self.pos[1], self.pos[0], self.newPos[1])
+            dc.DrawLine(self.pos[0], self.pos[1], self.newPos[0], self.pos[1])
+            dc.DrawLine(self.newPos[0], self.newPos[1], self.pos[0], self.newPos[1])
+            dc.DrawLine(self.newPos[0], self.newPos[1], self.newPos[0], self.pos[1])
+            
+    def OnSize(self,evt):
+        self.InitBuffer()
+    
+    def OnLeftDown(self,evt):
+        point = (evt.GetX(), evt.GetY())
+        
+        if point[1]>= float(0) and point[1]<= float(self.h):
+            if point[0]>= float(0) and point[0]<= float(self.w):
+                self._mouseIn = True
+                
+        if self._mouseIn:
+            self.CaptureMouse()
+            self.pos = point
+        
+    def OnLeftUp(self,evt):
+        if self.GetCapture():
+            self.ReleaseMouse()
+            self._mouseIn = False
+            self._selectRec = False
+            self.setSelection()
+            self.SetGraphEditorData(self.property, self.curFrame)
+            
+    def OnMiddleDown(self,evt):
+        point = (evt.GetX(), evt.GetY())
+        
+        if point[1]>= float(0) and point[1]<= float(self.h):
+            if point[0]>= float(0) and point[0]<= float(self.w):
+                self._mouseIn = True
+                
+        if self._mouseIn:
+            self.CaptureMouse()
+            self.midPos = point
+    
+    def OnMiddleUp(self, evt):
+        if self.GetCapture():
+            self.ReleaseMouse()
+    
+    def OnMotion(self,evt):
+        self._mouseIn = False
+        if evt.Dragging() and evt.LeftIsDown():
+            self.newPos = (evt.GetX(), evt.GetY())
+            if self.newPos[1]>= float(0) and self.newPos[1]<= float(self.h):
+                if self.newPos[0]>= float(0) and self.newPos[0]<= float(self.w):
+                    self._mouseIn = True
+            
+            if self._mouseIn:
+                if self.newPos == self.pos:
+                    evt.Skip()
+                    self._mouseIn = False
+                else:
+                    self._selectRec = True
+                    self.SetGraphEditorData(self.property,  self.curFrame)
+        
+        if evt.Dragging() and evt.MiddleIsDown():
+            self.newMidPos = (evt.GetX(), evt.GetY())
+            if self.newMidPos[1]>= float(0) and self.newMidPos[1]<= float(self.h):
+                if self.newMidPos[0]>= float(0) and self.newMidPos[0]<= float(self.w):
+                    self._mouseIn = True
+            
+            if self._mouseIn:
+                if self.newMidPos == self.midPos:
+                    evt.Skip()
+                    self._mouseIn = False
+                else:
+                    self.recalculateSlope()
+                    self.onAnimation()
+                    self.midPos = self.newMidPos
+        
+        evt.Skip()
+        self._mouseIn = False 
+        self._selectRec = False
+
+    def setExistKey(self, list):
+        flag = False
+        for i in range(len(list)):
+            if list[i][AG.KEYFRAME][AG.SELECT] == 1:
+                inside = self.inside(self.pos, self.newPos, (list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]))
+                if inside == True:
+                    list[i][AG.KEYFRAME][AG.SELECT] = 0
+                if inside == False:
+                    find = False
+                    for j in range(3,5):
+                        inside = self.inside(self.pos, self.newPos, (list[i][j][AG.LOCAL_VALUE][0], list[i][j][AG.LOCAL_VALUE][1]))
+                        if inside == False:
+                            list[i][j][AG.SELECT] = 0
+                        if inside == True: 
+                            list[i][j][AG.SELECT] = 1
+                            find = True
+                            flag = True
+                    if find == False:
+                        list[i][AG.KEYFRAME][AG.SELECT] == 0
+        
+        return flag          
+            
+    def setNewKey(self, list):
+        for i in range(len(list)):
+            inside = self.inside(self.pos, self.newPos, (list[i][2][0][0], list[i][2][0][1]))
+            if inside == True:
+                list[i][AG.KEYFRAME][AG.SELECT] = 1
+            if inside == False:
+                list[i][AG.KEYFRAME][AG.SELECT] = 0
+                
+    def setSelection(self):
+        if self.property == self._mainDialog.namestr:
+           self.setSelectionBase(self.X)
+           self.setSelectionBase(self.Y)
+           self.setSelectionBase(self.Z)
+           return
+        if self.property == property[AG.X]:
+           self.setSelectionBase(self.X)
+           return
+        if self.property == property[AG.Y]:
+           self.setSelectionBase(self.Y)
+           return
+        if self.property == property[AG.Z]:
+           self.setSelectionBase(self.Z)
+           return 
+       
+    def setSelectionBase(self, list):
+        self.setExistKey(list)
+        if self.setExistKey(list) == True:
+            return
+        else:
+            self.setNewKey(list)
+        
+    def inside(self, point0, point1, point):
+        if point0[0]<=point1[0] and point0[1]<=point1[1]:
+            if point0[0]<point[0] and point[0]<point1[0] and point0[1]<point[1] and point[1]<point1[1]:
+                return True
+            else:
+                return False
+        elif point1[0]<=point0[0] and point0[1]<=point1[1]:
+            if point1[0]<point[0] and point[0]<point0[0] and point0[1]<point[1] and point[1]<point1[1]:
+                return True
+            else:
+                return False
+        elif point0[0]<=point1[0] and point1[1]<=point0[1]:
+            if point0[0]<point[0] and point[0]<point1[0] and point1[1]<point[1] and point[1]<point0[1]:
+                return True
+            else:
+                return False
+        elif point1[0]<=point0[0] and point1[1]<=point0[1]:
+            if point1[0]<point[0] and point[0]<point0[0] and point1[1]<point[1] and point[1]<point0[1]:
+                return True
+            else:
+                return False
+        else:
+            return False
+        
+    def recalculateSlope(self):
+        if self.property == self._mainDialog.namestr:
+           self.recalculateSlopeBase(self.X)
+           self.recalculateSlopeBase(self.Y)
+           self.recalculateSlopeBase(self.Z)
+           return
+        if self.property == property[AG.X]:
+           self.recalculateSlopeBase(self.X)
+           return
+        if self.property == property[AG.Y]:
+           self.recalculateSlopeBase(self.Y)
+           return
+        if self.property == property[AG.Z]:
+           self.recalculateSlopeBase(self.Z)
+           return 
+    
+    def recalculateSlopeBase(self, list):
+        #recalculate the tangent slope
+        moveX = self.newMidPos[0]-self.midPos[0]
+        moveY = self.newMidPos[1]-self.midPos[1]
+        
+        for i in range(len(list)):
+            if list[i][AG.KEYFRAME][AG.SELECT] == 1:
+                if list[i][AG.IN_TANGENT][AG.SELECT] == 1:
+                    newPointX = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0] + moveX
+                    newPointY = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1] + moveY
+                    
+                    newSlope = [list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] - newPointX , newPointY - list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]]
+                    
+                    temp0 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0]
+                    temp1 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] 
+                    self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = newSlope[0]/self.unitWidth
+                    self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = newSlope[1]/self.unitHeight
+                    handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
+                    if handler[1][0][0]>= list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
+                        self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = temp0
+                        self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = temp1
+                        return
+                    if handler[1][0][0] < list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
+                        if self._OneTangent == False:
+                            list[i][AG.IN_TANGENT][0] = handler[1][0]
+                            list[i][AG.IN_SLOPE][0] = handler[3][0]
+                            list[i][AG.IN_SLOPE][1] = handler[3][1]
+                        
+                        if self._OneTangent == True:
+                            self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = newSlope[0]/self.unitWidth
+                            self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = newSlope[1]/self.unitHeight
+                            handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
+                            list[i][AG.IN_TANGENT][0] = handler[1][0]
+                            list[i][AG.OUT_TANGENT][0] = handler[2][0]
+                            list[i][AG.IN_SLOPE][0] = handler[3][0]
+                            list[i][AG.IN_SLOPE][1] = handler[3][1]
+                            list[i][AG.OUT_SLOPE][0] = handler[4][0]
+                            list[i][AG.OUT_SLOPE][1] = handler[4][1]
+                    
+                        self.SetGraphEditorData(self.property, self.curFrame)
+                    
+                if list[i][AG.OUT_TANGENT][AG.SELECT] == 1:
+                    newPointX = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0] + moveX
+                    newPointY = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1] + moveY
+
+                    newSlope = [newPointX  - list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] , list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] - newPointY]   
+
+                    temp0 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0]
+                    temp1 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] 
+                    self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = newSlope[0]/self.unitWidth
+                    self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = newSlope[1]/self.unitHeight
+
+                    handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
+                    if handler[2][0][0] <= list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
+                        self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = temp0
+                        self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = temp1
+                        return
+                    if handler[2][0][0] > list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
+                        if self._OneTangent == False:
+                            list[i][AG.OUT_TANGENT][0] = handler[2][0]
+                            list[i][AG.OUT_SLOPE][0] = handler[4][0]
+                            list[i][AG.OUT_SLOPE][1] = handler[4][1]
+                        
+                        if self._OneTangent == True:
+                            self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = newSlope[0]/self.unitWidth
+                            self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = newSlope[1]/self.unitHeight
+                            handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
+                            list[i][AG.IN_TANGENT][0] = handler[1][0]
+                            list[i][AG.OUT_TANGENT][0] = handler[2][0]
+                            list[i][AG.IN_SLOPE][0] = handler[3][0]
+                            list[i][AG.IN_SLOPE][1] = handler[3][1]
+                            list[i][AG.OUT_SLOPE][0] = handler[4][0]
+                            list[i][AG.OUT_SLOPE][1] = handler[4][1]
+
+                        self.SetGraphEditorData(self.property,  self.curFrame)
+
+    def selectHandler(self):
+        self._selectHandler = False
+        
+    def onAnimation(self):
+        if self._mainDialog.editor.mode == self._mainDialog.editor.ANIM_MODE:
+            self._mainDialog.editor.ui.animUI.OnAnimation(self._mainDialog.editor.ui.animUI.curFrame)
+        else:
+            pass
+        
+        
+class GraphEditorUI(wx.Dialog):
+    """
+    This is the graph editor main class implementation.
+    """
+    def __init__(self, parent, editor, object):
+        wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Graph Editor",
+                           pos=wx.DefaultPosition, size=(735, 535))
+                           
+        self.editor = editor
+        self.editor.GRAPH_EDITOR = True
+        self.object = object
+        self.xRange = 24+1
+        self.yRange = 50
+        if self.editor.mode == self.editor.ANIM_MODE:
+            self.curFrame = self.editor.ui.animUI.curFrame
+        self.curFrame = 1
+        
+        self.mainPanel1 = wx.Panel(self, -1)
+        
+        bmpZoomIn = ZoomIn.GetBitmap()
+        bmpZoomOut = ZoomOut.GetBitmap()
+        bmpOneTangent = OneTangent.GetBitmap()
+        bmpTwoTangents = TwoTangents.GetBitmap()
+        
+        self.buttonZoomIn = wx.BitmapButton(self.mainPanel1, -1, bmpZoomIn, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonZoomOut = wx.BitmapButton(self.mainPanel1, -1, bmpZoomOut, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonOneTangent = wx.BitmapButton(self.mainPanel1, -1, bmpOneTangent, size = (30,30),style = wx.BU_AUTODRAW)
+        self.buttonTwoTangents = wx.BitmapButton(self.mainPanel1, -1, bmpTwoTangents, size = (30,30),style = wx.BU_AUTODRAW)
+        
+        self.mainPanel2 = wx.Panel(self, -1)
+        
+        self.tree =  self.tree = wx.TreeCtrl(self.mainPanel2, id=-1, pos=wx.DefaultPosition,size=wx.Size(200, 450), style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE,validator=wx.DefaultValidator, name="treeCtrl")
+        self.namestr = "%s"%(object[OG.OBJ_DEF].name)
+        self.root = self.tree.AddRoot(self.namestr)
+        self.AddTreeNodes(self.root, property)
+        self.tree.Expand(self.root)
+        self.tree.SelectItem(self.root,select=True)
+        self.str = self.tree.GetItemText(self.root)
+        
+        self.graphEditorWindow =GraphEditorWindow(self.mainPanel2, wx.Size(500, 450), str(object[OG.OBJ_DEF].name), self.xRange, self.yRange, self.curFrame, self.object)
+        
+        self.SetProperties()
+        self.DoLayout()
+        
+        self.Bind(wx.EVT_BUTTON, self.OnZoomIn, self.buttonZoomIn)
+        self.Bind(wx.EVT_BUTTON, self.OnZoomOut, self.buttonZoomOut)
+        self.Bind(wx.EVT_BUTTON, self.OnOneTangent, self.buttonOneTangent)
+        self.Bind(wx.EVT_BUTTON, self.OnTwoTangents, self.buttonTwoTangents)
+       
+        self.Bind(wx.EVT_CLOSE, self.OnExit)
+        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
+        
+    def SetProperties(self):
+        pass
+    
+    def DoLayout(self):
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer1 = wx.FlexGridSizer(1, 4, 0, 0)
+        mainSizer2 = wx.FlexGridSizer(1, 2, 0, 0)
+        
+        mainSizer1.Add(self.buttonOneTangent, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 570)
+        mainSizer1.Add(self.buttonTwoTangents, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 10)
+        mainSizer1.Add(self.buttonZoomIn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 10)
+        mainSizer1.Add(self.buttonZoomOut, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10)
+        
+        mainSizer2.Add(self.tree, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10)
+        mainSizer2.Add(self.graphEditorWindow, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 10)
+        
+        self.mainPanel1.SetSizerAndFit(mainSizer1)
+        self.mainPanel2.SetSizer(mainSizer2)
+        
+        dialogSizer.Add(self.mainPanel2, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 10)
+        dialogSizer.Add(self.mainPanel1, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 5)
+        
+        self.SetSizer(dialogSizer)
+        self.Layout()
+
+        self.dialogSizer = dialogSizer
+    
+    def AddTreeNodes(self, parentItem, items):
+        for item in items:
+            if type(item) == str:
+                self.tree.AppendItem(parentItem, item)
+                
+    def OnSelChanged(self, evt):
+        item = evt.GetItem()
+        if item:
+            self.str = self.tree.GetItemText(item)
+            self.graphEditorWindow.refresh()
+            self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+    
+    def OnZoomIn(self,evt):
+        self.graphEditorWindow.zoom = self.graphEditorWindow.zoom/float(1.2)
+        self.graphEditorWindow.unitWidth = self.graphEditorWindow.unitWidth*float(1.2)
+        self.graphEditorWindow.unitHeight = self.graphEditorWindow.unitHeight*float(1.2)
+        self.graphEditorWindow.generateInfo()
+        self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+        
+    def OnZoomOut(self,evt):
+        self.graphEditorWindow.zoom = self.graphEditorWindow.zoom*float(1.2)
+        self.graphEditorWindow.unitWidth = self.graphEditorWindow.unitWidth/float(1.2)
+        self.graphEditorWindow.unitHeight = self.graphEditorWindow.unitHeight/float(1.2)
+        self.graphEditorWindow.generateInfo()
+        self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+    
+    def OnOneTangent(self,evt):
+        self.graphEditorWindow._OneTangent = True
+        self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+        
+    def OnTwoTangents(self,evt):
+        self.graphEditorWindow._OneTangent = False 
+        self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+        
+    def curFrameChange(self):
+        if self.editor.mode == self.editor.ANIM_MODE:
+            self.curFrame = self.editor.ui.animUI.curFrame
+            self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame)
+        else:
+            pass
+                  
+    def OnExit(self,evt):
+        self.Destroy()
+        self.editor.ui.graphEditorMenuItem.Check(False)
+        self.object = None
+        self.editor.GRAPH_EDITOR = False
+       
+
+
+        
+
+
+
+  

+ 2 - 0
direct/src/leveleditor/LevelEditor.py

@@ -8,6 +8,7 @@ to be game specific.
 from LevelEditorUI import *
 from LevelEditorBase import *
 from ObjectMgr import *
+from AnimMgr import *
 from ObjectHandler import *
 from ObjectPalette import *
 from ProtoPalette import *
@@ -23,6 +24,7 @@ class LevelEditor(LevelEditorBase):
         # If you have your own ObjectPalette and ObjectHandler
         # connect them in your own LevelEditor class
         self.objectMgr = ObjectMgr(self)
+        self.animMgr = AnimMgr(self)
         self.objectPalette = ObjectPalette()
         self.objectHandler = ObjectHandler(self)
         self.protoPalette = ProtoPalette()

+ 4 - 0
direct/src/leveleditor/LevelEditorBase.py

@@ -37,6 +37,8 @@ class LevelEditorBase(DirectObject):
         self.BASE_MODE = BitMask32.bit(0)
         self.CREATE_CURVE_MODE = BitMask32.bit(2)
         self.EDIT_CURVE_MODE = BitMask32.bit(3)
+        self.ANIM_MODE = BitMask32.bit(4)
+        self.GRAPH_EDITOR = False
         
         self.mode = self.BASE_MODE
         self.preMode = None
@@ -245,8 +247,10 @@ class LevelEditorBase(DirectObject):
                     return
 
         base.direct.deselectAll()
+        base.direct.selected.last = None
         self.ui.reset()
         self.objectMgr.reset()
+        self.animMgr.reset()
         self.actionMgr.reset()
         self.ui.perspView.camera.setPos(-19, -19, 19)
         self.ui.perspView.camera.lookAt(Point3(0, 0, 0))

+ 51 - 3
direct/src/leveleditor/LevelEditorUIBase.py

@@ -14,6 +14,9 @@ from LayerEditorUI import *
 from HotKeyUI import *
 from ProtoPaletteUI import *
 from ActionMgr import *
+from AnimControlUI import *
+from CurveAnimUI import * 
+from GraphEditorUI import *
 
 class PandaTextDropTarget(wx.TextDropTarget):
     def __init__(self, editor, view):
@@ -118,6 +121,10 @@ ID_PARENT_TO_SELECTED = 306
 
 ID_CREATE_CURVE = 601
 ID_EDIT_CURVE = 602
+ID_CURVE_ANIM = 603
+
+ID_ANIM = 701
+ID_GRAPH = 702
 
 class LevelEditorUIBase(WxPandaShell):
     """ Class for Panda3D LevelEditor """ 
@@ -140,7 +147,10 @@ class LevelEditorUIBase(WxPandaShell):
             ID_HOT_KEYS : ("&Hot Keys", None),
             ID_PARENT_TO_SELECTED : ("&Parent To Selected", None),
             ID_CREATE_CURVE : ("&Create Curve", None),
-            ID_EDIT_CURVE : ("&Edit Curve", None)
+            ID_EDIT_CURVE : ("&Edit Curve", None),
+            ID_CURVE_ANIM : ("&Curve Animation", None),
+            ID_ANIM : ("&Edit Animation", None),
+            ID_GRAPH : ("&Graph Editor", None)
             })
 
         self.editor = editor
@@ -217,8 +227,45 @@ class LevelEditorUIBase(WxPandaShell):
         
         self.editCurveMenuItem = self.menuCurve.Append(ID_EDIT_CURVE, self.MENU_TEXTS[ID_EDIT_CURVE][0], kind = wx.ITEM_CHECK)
         self.Bind(wx.EVT_MENU, self.onEditCurve, self.editCurveMenuItem)
-
+        
+        self.curveAnimMenuItem = self.menuCurve.Append(ID_CURVE_ANIM, self.MENU_TEXTS[ID_CURVE_ANIM][0], kind = wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.onCurveAnim, self.curveAnimMenuItem)
+        
+        self.menuAnim = wx.Menu()
+        self.menuBar.Insert(4, self.menuAnim, "&AnimationMode")
+        
+        self.editAnimMenuItem = self.menuAnim.Append(ID_ANIM, self.MENU_TEXTS[ID_ANIM][0], kind = wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.onAnimation, self.editAnimMenuItem)
+        
+        self.graphEditorMenuItem = self.menuAnim.Append(ID_GRAPH, self.MENU_TEXTS[ID_GRAPH][0], kind = wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.onGraphEditor, self.graphEditorMenuItem)
+        
         WxPandaShell.createMenu(self)
+    
+    def onGraphEditor(self,e):
+        if base.direct.selected.last == None:
+            dlg = wx.MessageDialog(None, 'Please select a object first.', 'NOTICE', wx.OK )
+            dlg.ShowModal()
+            dlg.Destroy()
+            self.graphEditorMenuItem.Check(False)
+        else:
+            currentObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
+            self.graphEditorUI = GraphEditorUI(self, self.editor, currentObj)
+            self.graphEditorUI.Show()
+            self.graphEditorMenuItem.Check(True)
+    
+    def onAnimation(self,e):
+        if self.editor.mode != self.editor.ANIM_MODE:
+            self.animUI = AnimControlUI(self, self.editor)
+            self.animUI.Show()
+            self.editor.mode = self.editor.ANIM_MODE
+        if self.editor.mode == self.editor.ANIM_MODE:
+            self.editAnimMenuItem.Check(True)
+        
+    def onCurveAnim(self,e):
+        self.curveAnimUI = CurveAnimUI(self, self.editor)
+        self.curveAnimUI.Show()
+        self.curveAnimMenuItem.Check(True)
         
     def onCreateCurve(self,e):
         """Function to invoke curve creating, need to check previous mode"""
@@ -248,7 +295,7 @@ class LevelEditorUIBase(WxPandaShell):
                     self.editCurveMenuItem.Check(False)
     
     def onEditCurve(self,e):
-        """Function to invoke curve editing and transfer global information to local information. Need to check previous mode"""
+        """Function to invoke curve editing and translate global information to local information. Need to check previous mode"""
         if self.editor.mode == self.editor.EDIT_CURVE_MODE:
             self.editCurveMenuItem.Check(False)
             self.editor.curveEditor.onBaseMode() 
@@ -639,3 +686,4 @@ class CurveDegreeUI(wx.Dialog):
         if(str(self.degree.GetSelection())=='2'):
             self.parent.editor.curveEditor.degree = 4
         self.Destroy()
+

+ 50 - 1
direct/src/leveleditor/ObjectMgrBase.py

@@ -31,8 +31,12 @@ class ObjectMgrBase:
         self.lastUid = ''
         self.lastUidMode = 0
         self.currNodePath = None   
-
         self.currLiveNP = None
+        
+        self.Actor = []
+        self.findActors(render)
+        self.Nodes = []
+        self.findNodes(render)
 
     def reset(self):
         base.direct.deselectAllCB()
@@ -50,6 +54,8 @@ class ObjectMgrBase:
         self.objects = {}
         self.npIndex = {}
         self.saveData = []
+        self.Actor = []
+        self.Nodes = []
 
     def genUniqueId(self):
         # [gjeon] to solve the problem of unproper $USERNAME
@@ -265,6 +271,17 @@ class ObjectMgrBase:
     def removeObjectById(self, uid):
         obj = self.findObjectById(uid)
         nodePath = obj[OG.OBJ_NP]
+        
+        for i in range(0,len(self.Actor)):
+            if self.Actor[i] == obj:
+                del self.Actor[i]
+                break
+        for i in range(0,len(self.Nodes)):
+            if self.Nodes[i][OG.OBJ_UID] == uid:
+                del self.Nodes[i]
+                break
+        self.editor.animMgr.removeAnimInfo(obj[OG.OBJ_UID])
+        
         del self.objects[uid]
         del self.npIndex[nodePath]
 
@@ -279,6 +296,16 @@ class ObjectMgrBase:
     def removeObjectByNodePath(self, nodePath):
         uid = self.npIndex.get(nodePath)
         if uid:
+            for i in range(0,len(self.Actor)):
+                if self.Actor[i][OG.OBJ_UID] == uid:
+                    del self.Actor[i]
+                    break
+            for i in range(0,len(self.Nodes)):
+                if self.Nodes[i][OG.OBJ_UID] == uid:
+                    del self.Nodes[i]
+                    break
+            self.editor.animMgr.removeAnimInfo(uid)
+            
             del self.objects[uid]
             del self.npIndex[nodePath]
 
@@ -880,5 +907,27 @@ class ObjectMgrBase:
     def flatten(self, newobjModel, model, objDef, uid):
         # override this to flatten models
         pass
+        
+    def findActors(self, parent):
+        for child in parent.getChildren():
+            if child.hasTag('OBJRoot') and not child.hasTag('Controller'):
+                obj = self.findObjectByNodePath(child)
+
+                if obj:
+                    if isinstance(obj[OG.OBJ_NP],Actor):
+                        self.Actor.append(obj)
+                    
+                self.findActors(child)
 
+        
+    def findNodes(self, parent):
+        for child in parent.getChildren():
+            if child.hasTag('OBJRoot') and not child.hasTag('Controller'):
+                obj = self.findObjectByNodePath(child)
 
+                if obj:
+                    self.Nodes.append(obj)
+                    
+                self.findActors(child)
+            
+                    

+ 63 - 0
direct/src/leveleditor/ObjectPropertyUI.py

@@ -5,11 +5,24 @@ import wx
 import os
 import math
 
+from wx.lib.embeddedimage import PyEmbeddedImage
 from wx.lib.scrolledpanel import ScrolledPanel
 from wx.lib.agw.cubecolourdialog import *
 from direct.wxwidgets.WxSlider import *
 from pandac.PandaModules import *
 import ObjectGlobals as OG
+import AnimGlobals as AG
+
+#----------------------------------------------------------------------
+Key = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAIAAACQKrqGAAAAA3NCSVQICAjb4U/gAAABIElE"
+    "QVQokZWSMW7CQBBFZ2Z3sQ02Ni4sOS6QiLgO5yBXIMcJ1KENje8QLESH7F3FVFQIIS3eTWGJ"
+    "VE7Iq6Z4+tL8GVRSwmPQg94fKiIOBoNer2et/U1FRER8X6+LonBdFwB4l+p53mq1qqRUUsZx"
+    "nKYpBwDOuRACEQGgaRoAYETn8/l4PL4uFkqp/X6fZRlnjO12u7KqENEa43keADDGvuo6Go0A"
+    "wPd9YkxrzY0x4/FYKlXX9eVymc1mjIiIgiD43G4BwFprmgYRubU2DMPnySTw/ev1+pSmRISI"
+    "SZJ8bDan06ksSyLiQmDXCfr9fp7nb8vldDp9mc9d1/1R27XaClscxzkcDlEUhcOhvt06U1uE"
+    "EMaYtpbOXlu01vf5Hz/wDRuDdIDl5WtQAAAAAElFTkSuQmCC")
+#----------------------------------------------------------------------
 
 class AnimFileDrop(wx.FileDropTarget):
     def __init__(self, editor):
@@ -55,13 +68,63 @@ class ObjectPropUI(wx.Panel):
         self.label = wx.StaticText(self.labelPane, label=label)
         self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
         self.labelSizer.Add(self.label)
+        bmpKey = Key.GetBitmap()
+        self.setKeyButton = wx.BitmapButton(self.labelPane, -1, bmpKey, size = (15,15),style = wx.BU_AUTODRAW)
+        self.labelSizer.Add(self.setKeyButton)
         self.labelPane.SetSizer(self.labelSizer)
         self.uiPane = wx.Panel(self)
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(self.labelPane)
         sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
         self.SetSizer(sizer)
+        
+        self.setKeyButton.Bind(wx.EVT_BUTTON, self.onKey)
+
+    def onKey(self,evt):
+        self.parent = wx.GetTopLevelParent(self)
+        if self.parent.editor.mode == self.parent.editor.ANIM_MODE:
+            obj= self.parent.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
+            
+            objUID = obj[OG.OBJ_UID]
+            propertyName = self.label.GetLabelText()
+            
+            value = self.getValue()
+            frame = self.parent.editor.ui.animUI.curFrame
+            
+            if self.parent.editor.animMgr.keyFramesInfo.has_key((objUID,propertyName)):
+                for i in range(len(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])):
+                    if self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i][AG.FRAME] == frame:
+                        del self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i]
+                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)].append([frame, value, [], []])
+                #sort keyFrameInfo list by the order of frame number
+                sortKeyList = self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)]
+                for i in range(0, len(sortKeyList)-1):
+                    for j in range(i+1, len(sortKeyList)):
+                        if sortKeyList[i][AG.FRAME]>sortKeyList[j][AG.FRAME]:
+                            temp = sortKeyList[i]
+                            sortKeyList[i] = sortKeyList[j]
+                            sortKeyList[j] = temp
+                            
+                self.parent.editor.animMgr.generateSlope(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])
+            else:
+                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)] = [[frame, value, [], []]]
+            
+            exist = False
+            for keyFrame in self.parent.editor.animMgr.keyFrames:
+                if frame == keyFrame:
+                    exist = True
+                    break
+            
+            if exist == False:
+                self.parent.editor.animMgr.keyFrames.append(frame)
+                self.parent.editor.ui.animUI.OnPropKey()
 
+            else:
+                self.parent.editor.ui.animUI.OnPropKey()
+                
+        else:
+            evt.Skip()
+        
     def setValue(self, value):
         self.ui.SetValue(value)