PageRenderTime 63ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/flash/Gobe.hx

http://github.com/brentp/gobe
Haxe | 447 lines | 361 code | 48 blank | 38 comment | 72 complexity | 272052f88f54ea9108e70785645ccc01 MD5 | raw file
  1. import flash.external.ExternalInterface;
  2. import flash.display.MovieClip;
  3. import flash.display.Sprite;
  4. import flash.display.Shape;
  5. import flash.display.Loader;
  6. import flash.display.StageScaleMode;
  7. import flash.display.StageAlign;
  8. import flash.display.Bitmap;
  9. import flash.display.BitmapData;
  10. import flash.events.Event;
  11. import flash.events.KeyboardEvent;
  12. import flash.events.MouseEvent;
  13. import flash.geom.Point;
  14. import flash.geom.Rectangle;
  15. import flash.net.URLRequest;
  16. import flash.net.URLLoader;
  17. import flash.system.LoaderContext;
  18. import flash.text.TextField;
  19. import flash.text.TextFormat;
  20. import flash.text.StyleSheet;
  21. import flash.utils.Timer;
  22. import flash.events.TimerEvent;
  23. import flash.events.IOErrorEvent;
  24. import Util;
  25. import Annotation;
  26. import Plot;
  27. import Track;
  28. class Gobe extends Sprite {
  29. public static var fontSize:Int = 16;
  30. public static var ctx = new LoaderContext(true);
  31. private var track:String;
  32. public var cnss:Array<Int>;
  33. public var stage_height:Int;
  34. public var stage_width:Float;
  35. public var wedge_alpha:Float;
  36. public var drag_sprite:DragSprite;
  37. public static var zi : flash.ui.ContextMenuItem;
  38. public var panel:Sprite; // holds the lines.
  39. public var feature_stylesheet:StyleSheet; // how to draw stuff..
  40. private var _all:Bool;
  41. public static var tracks:Hash<Track>;
  42. public static var annotations:Hash<Annotation>;
  43. public static var plots:Hash<Plot>;
  44. public static var styles:Hash<Style>; // {'CDS': CDSINFO }
  45. public static var edges = new Array<Edge>();
  46. public var annotations_url:String;
  47. public var style_url:String;
  48. // these are set programmatically based on sub_track_height_ratio;
  49. public static var anno_track_height:Int;
  50. public static var sub_track_height:Int;
  51. public function clearPanelGraphics(e:MouseEvent){
  52. while(panel.numChildren != 0){ panel.removeChildAt(0); }
  53. }
  54. public static function js_onclick(fid:String, fname:String, ftype:String, bpmin:Int, bpmax:Int, track_id:String){
  55. ExternalInterface.call('Gobe.onclick', [fid, fname, ftype, bpmin, bpmax, track_id]);
  56. }
  57. public static function js_warn(warning:String){
  58. ExternalInterface.call('Gobe.warn', warning);
  59. }
  60. public static function js_onmouseover(fid:String, fname:String, bpmin:Int, bpmax:Int, track_id:String){
  61. ExternalInterface.call('Gobe.onmouseover', fid, fname, bpmin, bpmax, track_id);
  62. }
  63. public static function main(){
  64. haxe.Firebug.redirectTraces();
  65. var stage = flash.Lib.current.stage;
  66. // this one the event gets called anywhere.
  67. stage.focus = stage.stage;
  68. stage.align = StageAlign.TOP_LEFT;
  69. stage.scaleMode = StageScaleMode.NO_SCALE;
  70. stage.addChild(new Gobe());
  71. }
  72. private function add_callbacks(){
  73. ExternalInterface.addCallback("set_hsp_colors", Util.set_hsp_colors);
  74. ExternalInterface.addCallback("redraw", redraw);
  75. ExternalInterface.addCallback("clear_wedges", clear_wedges);
  76. ExternalInterface.addCallback("set_data", set_data);
  77. ExternalInterface.addCallback("set_url", set_url);
  78. ExternalInterface.addCallback("hide_track_labels", Gobe.hideTrackLabels);
  79. ExternalInterface.addCallback("hide_subtrack_labels", Gobe.hideSubTrackLabels);
  80. }
  81. public function reset(){
  82. for(a in annotations.iterator()){ a.graphics.clear(); }
  83. // and remove edges.
  84. for(e in edges){ flash.Lib.current.removeChild(e); /*e.graphics.clear();*/ }
  85. for(t in tracks.iterator()){ t.clear(); }
  86. edges = new Array<Edge>();
  87. tracks = new Hash<Track>();
  88. annotations = new Hash<Annotation>();
  89. plots = new Hash<Plot>();
  90. }
  91. public function set_data(data:String){
  92. this.handleAnnotationData(data);
  93. }
  94. public function set_url(url:String){
  95. this.annotations_url = url;
  96. geturl(url, annotationReturn);
  97. }
  98. public function clear_wedges(){
  99. for(w in edges){ w.visible = false; }
  100. }
  101. public function redraw(){
  102. for(w in edges){ w.drawn = false; w.graphics.clear(); }
  103. for(a in annotations) { a.draw(); }
  104. }
  105. public function onMouseWheel(e:MouseEvent){
  106. var change = e.delta > 0 ? 1 : - 1;
  107. this.wedge_alpha += (change / 10.0);
  108. if(this.wedge_alpha > 1){ this.wedge_alpha = 1.0; }
  109. if(this.wedge_alpha < 0.1){ this.wedge_alpha = 0.1; }
  110. }
  111. public function onMouseMove(e:MouseEvent){
  112. var x = e.localX;
  113. var tid = tracks.keys().next();
  114. var t = tracks.get(tid);
  115. trace(x);
  116. }
  117. public function new(){
  118. super();
  119. var p = flash.Lib.current.loaderInfo.parameters;
  120. this.drag_sprite = new DragSprite();
  121. this.wedge_alpha = 0.3;
  122. this.annotations_url = p.annotations;
  123. this.style_url = Reflect.field(p, 'style') ? p.style : p.default_style ;
  124. panel = new Sprite();
  125. addChild(panel);
  126. addChild(this.drag_sprite);
  127. this.add_callbacks();
  128. var i:Int;
  129. tracks = new Hash<Track>();
  130. annotations = new Hash<Annotation>();
  131. plots = new Hash<Plot>();
  132. // the event only gets called when mousing over an HSP.
  133. var stage = flash.Lib.current.stage;
  134. addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
  135. stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  136. stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
  137. stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
  138. flash.Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyPress);
  139. this.stage_width = flash.Lib.current.stage.stage.stageWidth;
  140. this.stage_height = flash.Lib.current.stage.stage.stageHeight;
  141. feature_stylesheet = new StyleSheet();
  142. // loads style 2x if default style is same as style...
  143. Reflect.field(p, 'default_style') ? geturl(p.default_style, _defaultStyleReturn)
  144. : geturl(this.style_url, styleReturn);
  145. /*
  146. var cm = new flash.ui.ContextMenu();
  147. cm.hideBuiltInItems();
  148. zi = new flash.ui.ContextMenuItem("Zoom In", true, true, true);
  149. zi.addEventListener(flash.events.ContextMenuEvent.MENU_ITEM_SELECT, function (e:Event){
  150. trace('selected');
  151. }, false, 0, true);
  152. cm.customItems = [zi];
  153. trace(cm.customItems);
  154. flash.Lib.current.contextMenu = cm;
  155. */
  156. }
  157. private function _defaultStyleReturn(e:Event){
  158. feature_stylesheet.parseCSS(e.target.data);
  159. geturl(this.style_url, styleReturn);
  160. }
  161. public function styleReturn(e:Event) {
  162. feature_stylesheet.parseCSS(e.target.data);
  163. styles = new Hash<Style>();
  164. for(ftype in feature_stylesheet.styleNames){
  165. ftype = ftype.toLowerCase();
  166. var st = feature_stylesheet.getStyle(ftype);
  167. styles.set(ftype, new Style(ftype, st));
  168. }
  169. if(StringTools.startsWith(this.annotations_url, "javascript:")){
  170. // getting the data directly from javascript call..
  171. var jsfn = this.annotations_url.substr(9);
  172. var data = ExternalInterface.call(jsfn);
  173. handleAnnotationData(data);
  174. }
  175. else {
  176. geturl(this.annotations_url, annotationReturn);
  177. }
  178. }
  179. public static function hideTrackLabels(){
  180. for(k in tracks.keys()){
  181. var ttf = tracks.get(k).anno_track.ttf;
  182. ttf.visible = !ttf.visible;
  183. }
  184. }
  185. public static function hideSubTrackLabels(){
  186. for(t in tracks.iterator()){
  187. for(st in t.subtracks.iterator()){
  188. if(Type.getClass(st) != HSPTrack){ continue; }
  189. cast(st, HSPTrack).ttf.visible = !cast(st, HSPTrack).ttf.visible;
  190. }
  191. }
  192. }
  193. public function onKeyPress(e:KeyboardEvent){
  194. if(e.keyCode == 38 || e.keyCode == 40){ // up
  195. if(e.keyCode == 38 && Gobe.fontSize > 25){ return; }
  196. if(e.keyCode == 40 && Gobe.fontSize < 8){ return; }
  197. Gobe.fontSize += (e.keyCode == 38 ? 1 : - 1);
  198. for(k in tracks.keys()){
  199. tracks.get(k).anno_track.ttf.styleSheet.setStyle('p', {fontSize:Gobe.fontSize});
  200. }
  201. }
  202. // 'l'
  203. if(e.keyCode == 76){
  204. hideTrackLabels();
  205. }
  206. // 's'
  207. if(e.keyCode == 83){
  208. hideSubTrackLabels();
  209. }
  210. // 'escape'
  211. if(e.keyCode == 27){
  212. clear_wedges();
  213. }
  214. }
  215. public static function geturl(url:String, handler:Event -> Void){
  216. //trace("getting:" + url);
  217. url = StringTools.urlDecode(url);
  218. trace("getting:" + url);
  219. var ul = new URLLoader();
  220. ul.load(new URLRequest(url));
  221. ul.addEventListener(Event.COMPLETE, handler);
  222. ul.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event){
  223. Gobe.js_warn("failed to get:" + url + "\n" + e);
  224. });
  225. }
  226. private function initializeSubTracks(edge_tracks:Hash<Hash<Int>>){
  227. // so here, it knows all the annotations and edges, so we figure out
  228. // the subtracks it needs to show the relationships.
  229. var atrack_ids = Util.sorted_keys(tracks.keys());
  230. var colors = new Hash<UInt>();
  231. for(aid in atrack_ids){
  232. var atrack = tracks.get(aid);
  233. var i = 1;
  234. var ex = edge_tracks.exists(aid);
  235. var btrack_ids = ex ?
  236. Util.sorted_keys(edge_tracks.get(aid).keys()) : [];
  237. var ntracks = btrack_ids.length;
  238. for(bid in btrack_ids){
  239. var color_key = aid < bid ? aid + "|" + bid : bid + "|" + aid;
  240. // TODO: allow getting this from css.
  241. var track_color = Util.next_track_color(aid, bid, colors);
  242. var btrack = tracks.get(bid);
  243. for(strand in ['+', '-']){
  244. var sub = new HSPTrack(atrack, btrack, Gobe.sub_track_height);
  245. //sub.fill_color = Util.track_colors[btrack.i];
  246. sub.fill_color = track_color;
  247. atrack.subtracks.set(strand + bid, sub);
  248. atrack.addChildAt(sub, 0);
  249. if (strand == '+'){
  250. // start from top, goes to middle
  251. sub.y = Options.info_track_height + i * Gobe.sub_track_height;
  252. }
  253. else {
  254. // start from bottom, goes to middle
  255. sub.y = atrack.track_height - (ntracks - i) * Gobe.sub_track_height;
  256. }
  257. sub.draw();
  258. }
  259. i += 1;
  260. }
  261. // now initialize the tracks for +/- annotations.
  262. i -= 1;
  263. var at = new AnnoTrack(atrack, Gobe.anno_track_height);
  264. // why does this work? i dont know.
  265. at.y = Options.info_track_height + i * Gobe.sub_track_height + Gobe.anno_track_height / 2;
  266. var eidx = 0;
  267. for(eti in atrack.extra_anno_track_ids){
  268. var extra_anno = new AnnoTrack(atrack, Gobe.anno_track_height, eti);
  269. extra_anno.y = at.y + (Gobe.anno_track_height * ++eidx);
  270. }
  271. }
  272. }
  273. public static function addPlot(plot:Plot){
  274. plot.track = Gobe.tracks.get(plot.track_id);
  275. var st = plot.track.subtracks.get((plot.strand == 1) ? '+' : ((plot.strand == -1) ? '-' : '0'));
  276. plot.subtrack = st;
  277. // NOTE: always putting the plot at the bottom...
  278. st.addChildAt(plot, 0);
  279. }
  280. public static inline function get_strand(a:Annotation):String {
  281. return ((a.strand == 1) ? '+' : ((a.strand == -1) ? '-' : '0'));
  282. }
  283. private function addAnnotations(arr:Array<Annotation>){
  284. var a:Annotation;
  285. arr.sort(function(a:Annotation, b:Annotation):Int {
  286. return a.style.zindex < b.style.zindex ? -1 : 1;
  287. });
  288. for(a in arr){
  289. if(! a.is_hsp){
  290. /*for(k in a.track.subtracks.keys()){
  291. trace(k);
  292. }*/
  293. var sub = a.track.subtracks.get(a.subanno_id + ((a.strand == 1) ? '+' : ((a.strand == -1) ? '-' : '0')));
  294. a.subtrack = sub;
  295. sub.addChild(a);
  296. }
  297. else {
  298. // loop over the pairs and add to appropriate subtrack based on the id of other.
  299. for(edge_id in a.edges){
  300. var edge = edges[edge_id];
  301. var other:Annotation = edge.a == a ? edge.b : edge.a;
  302. var strand = ((other.strand == 1) ? '+' : ((other.strand == -1) ? '-' : '0'));
  303. var sub = a.track.subtracks.get(strand + other.track.id);
  304. a.subtrack = sub;
  305. sub.addChild(a);
  306. }
  307. }
  308. }
  309. }
  310. public function annotationReturn(e:Event){
  311. handleAnnotationData(e.target.data);
  312. }
  313. public static function set_anno_style(a:Annotation){
  314. var astyle = styles.get(a.ftype);
  315. if (astyle == null && a.is_hsp){
  316. astyle = styles.get('hsp');
  317. }
  318. if(astyle == null){
  319. Gobe.js_warn("no style defined for:" + a.ftype);
  320. a.style = styles.get('default');
  321. }
  322. else {
  323. a.style = astyle;
  324. }
  325. if(Gobe.annotations.exists(a.id)) {
  326. Gobe.js_warn(a.id + " is not a unique annotation id. overwriting");
  327. }
  328. Gobe.annotations.set(a.id, a);
  329. // we set the edges implicitly based on consecutive hsps.
  330. }
  331. public function handleAnnotationData(data:String){
  332. var t = haxe.Timer.stamp();
  333. var lines:Array<String> = StringTools.ltrim(data).split("\n");
  334. if(!Lambda.empty(annotations)){ this.reset(); }
  335. var anno_lines:Array<Array<String>> = [];
  336. for(l in lines){ if (!(l.length == 0 || l.charAt(0) == "#")) { anno_lines.push(l.split(",")); } }
  337. var hsps = new Array<Annotation>();
  338. var edge_tracks = new Hash<Hash<Int>>();
  339. var anarr = Util.add_tracks_from_annos(anno_lines, edge_tracks);
  340. initializeSubTracks(edge_tracks);
  341. addAnnotations(anarr);
  342. }
  343. public function mouseMove(e:MouseEvent){
  344. if(! e.buttonDown){ return; }
  345. //trace(e.stageX + "," + e.stageY);
  346. var r = this.drag_sprite.do_draw(e.stageX, e.stageY);
  347. drawEdgesInRect(r);
  348. }
  349. public function mouseDown(e:MouseEvent){
  350. var d = this.drag_sprite;
  351. d.graphics.clear();
  352. d.startx = e.stageX;
  353. d.starty = e.stageY;
  354. }
  355. public function mouseUp(e:MouseEvent){
  356. this.drag_sprite.graphics.clear();
  357. }
  358. public function drawEdgesInRect(r:Rectangle){
  359. for(ed in edges){
  360. ed.visible = false;
  361. if(r.intersects(ed.a.getRect(this)) || r.intersects(ed.b.getRect(this))){
  362. ed.draw();
  363. }
  364. }
  365. }
  366. }
  367. class MTextField extends TextField {
  368. public function new(){
  369. super();
  370. this.styleSheet = new StyleSheet();
  371. }
  372. }
  373. // this makes the gray selection rectangle.
  374. class DragSprite extends Sprite {
  375. public var startx:Float;
  376. public var starty:Float;
  377. public function new(){
  378. super();
  379. }
  380. public function do_draw(eX:Float, eY:Float){
  381. this.graphics.clear();
  382. this.graphics.lineStyle(1, 0xcccccc);
  383. var xmin = Math.min(this.startx, eX);
  384. var xmax = Math.max(this.startx, eX);
  385. var ymin = Math.min(this.starty, eY);
  386. var ymax = Math.max(this.starty, eY);
  387. this.graphics.beginFill(0xcccccc, 0.2);
  388. this.graphics.drawRect(xmin, ymin, xmax - xmin, ymax - ymin);
  389. this.graphics.endFill();
  390. return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
  391. }
  392. }
  393. class GEvent extends Event {
  394. public static var LOADED = "LOADED";
  395. //public static var ALL_LOADED = "ALL_LOADED";
  396. public function new(type:String){
  397. super(type);
  398. }
  399. }