PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/transition9/util/DisplayUtils.hx

http://github.com/dionjwa/Hydrax
Haxe | 605 lines | 405 code | 82 blank | 118 comment | 126 complexity | d4371a83304a194705f0afb1bfb3d7e3 MD5 | raw file
  1. /*******************************************************************************
  2. * Hydrax: haXe port of the PushButton Engine
  3. * Copyright (C) 2010 Dion Amago
  4. * For more information see http://github.com/dionjwa/Hydrax
  5. *
  6. * This file is licensed under the terms of the MIT license, which is included
  7. * in the License.html file at the root directory of this SDK.
  8. ******************************************************************************/
  9. package org.transition9.util;
  10. import de.polygonal.core.math.Mathematics;
  11. import flash.display.Bitmap;
  12. import flash.display.BitmapData;
  13. import flash.display.DisplayObject;
  14. import flash.display.DisplayObjectContainer;
  15. import flash.display.Loader;
  16. import flash.display.Sprite;
  17. import flash.events.IOErrorEvent;
  18. import flash.geom.Matrix;
  19. import flash.geom.Point;
  20. import flash.geom.Rectangle;
  21. import flash.net.URLRequest;
  22. import flash.text.TextField;
  23. using Lambda;
  24. using org.transition9.util.ArrayUtil;
  25. using org.transition9.util.IterUtil;
  26. class DisplayUtils
  27. {
  28. public static var BOTTOM_TO_TOP:Float = 3;
  29. public static var LEFT_TO_RIGHT:Float = 0;
  30. public static var RIGHT_TO_LEFT:Float = 1;
  31. public static var TOP_TO_BOTTOM:Float = 2;
  32. public static function isAbove (d1 :DisplayObject, d2 :DisplayObject) :Bool
  33. {
  34. //Break out early
  35. if (d1 == d2 || d1 == null || d2 == null || d1.parent == null || d2.parent == null) {
  36. return false;
  37. }
  38. var currentParent :DisplayObjectContainer = d1.parent;
  39. var parents1 :Array<DisplayObject> = [d1];
  40. //Craate the parentage chain for d1
  41. while(currentParent != null && currentParent != flash.Lib.current.stage) {
  42. parents1.push(currentParent);
  43. currentParent = currentParent.parent;
  44. }
  45. //Now go through d2, checking if the current parent is in the list
  46. currentParent = d2.parent;
  47. var lastCommonAncestorChildD2 :DisplayObject = d2;
  48. while(!parents1.has(currentParent)) {
  49. lastCommonAncestorChildD2 = currentParent;
  50. currentParent = currentParent.parent;
  51. }
  52. //Now that that's done, the current parent is the last common ancestor
  53. var lastCommonAncestorChildD1 :DisplayObject = parents1[parents1.indexOf(currentParent) - 1];
  54. return currentParent.getChildIndex(lastCommonAncestorChildD1) > currentParent.getChildIndex(lastCommonAncestorChildD2);
  55. }
  56. /**
  57. * Variation of com.threerings.display.DisplayUtil.applyToHierarchy.
  58. *
  59. * Instead of stopping completely if the callback returns true, only
  60. * stop delving into the display hierarchy, but keep applying to siblings.
  61. *
  62. * Call the specified function for the display object and all descendants.
  63. *
  64. * This is nearly exactly like mx.utils.DisplayUtil.walkDisplayObjects,
  65. * except this method copes with security errors when examining a child.
  66. *
  67. * @param callbackFunction Signature:
  68. * function (disp :DisplayObject) :void
  69. * or
  70. * function (disp :DisplayObject) :Boolean
  71. *
  72. * If you return a Boolean, you may return <code>true</code> to indicate that you've
  73. * found what you were looking for, and halt iteration.
  74. *
  75. * @return true if iteration was halted by callbackFunction returning true
  76. */
  77. public static function applyToHierarchy (disp :DisplayObject,
  78. callbackFunction :DisplayObject->Dynamic) :Bool
  79. {
  80. // halt iteration if callbackFunction returns true
  81. if (callbackFunction(disp)) {
  82. return true;
  83. }
  84. if (Std.is( disp, DisplayObjectContainer)) {
  85. var container:DisplayObjectContainer = cast( disp, DisplayObjectContainer);
  86. var nn = container.numChildren;
  87. for (ii in 0...Std.int(nn)) {
  88. try {
  89. disp = container.getChildAt(ii);
  90. } catch (err :Dynamic) {
  91. continue;
  92. }
  93. // and then we apply outside of the try/catch block so that
  94. // we don't hide errors thrown by the callbackFunction.
  95. applyToHierarchy(disp, callbackFunction);
  96. }
  97. }
  98. return false;
  99. }
  100. public static function boundsUnion (displayObjects :Array<Dynamic>, ?relativeTo :DisplayObject =
  101. null) :Rectangle
  102. {
  103. var bounds:Rectangle = null;
  104. for (disp in displayObjects) {
  105. var dispBounds:Rectangle =
  106. disp.getBounds(if(relativeTo == null) disp.stage else relativeTo);
  107. if (bounds != null) {
  108. bounds = bounds.union(dispBounds);
  109. } else {
  110. bounds = dispBounds;
  111. }
  112. }
  113. return bounds;
  114. }
  115. public static function centerOn (d :DisplayObject, ?x :Float = 0, ?y :Float = 0) :DisplayObject
  116. {
  117. var bounds:Rectangle;
  118. if (d.parent != null) {
  119. #if (flash || spaceport)
  120. bounds = d.getBounds(d.parent != null ? d.parent : d);
  121. #elseif cpp
  122. bounds = d.nmeGetPixelBounds();
  123. #end
  124. var boundsCenterX = bounds.left + bounds.width / 2;
  125. var xDiff = d.x - boundsCenterX;
  126. d.x = x + xDiff;
  127. var boundsCenterY = bounds.top + bounds.height / 2;
  128. var yDiff = d.y - boundsCenterY;
  129. d.y = y + yDiff;
  130. } else { //If we're not attached to a parent yet, centering will be ok unless
  131. //the displayObject is scaled
  132. d.x = x;
  133. d.y = y;
  134. #if (flash || spaceport)
  135. bounds = d.getBounds(d);
  136. #elseif cpp
  137. bounds = d.nmeGetPixelBounds();
  138. #end
  139. d.x -= (bounds.width / 2) + bounds.left;
  140. d.y -= (bounds.height / 2) + bounds.top;
  141. }
  142. return d;
  143. }
  144. /**
  145. * Converts any DisplayObject into a Bitmap. This can increase the graphical
  146. * performance of complex MovieClips.
  147. */
  148. public static function convertToBitmap (d :DisplayObject, ?scale :Float = 1) :Bitmap
  149. {
  150. if (d == null) {
  151. return null;
  152. }
  153. var bd = BitmapUtil.createBitmapData(d, scale);
  154. return bd != null ? new Bitmap(bd) : null;
  155. }
  156. public static function detach (d :DisplayObject) :Void
  157. {
  158. if (d != null && d.parent != null) {
  159. d.parent.removeChild(d);
  160. }
  161. }
  162. public static function distribute (seq :Array<Dynamic>, startX :Float, startY :Float, endX :Float,
  163. endY :Float) :Void
  164. {
  165. if (seq == null || seq.length == 0) {
  166. return;
  167. }
  168. var xInc = (endX - startX) / (seq.length + 1);
  169. startX += xInc / 2;
  170. var yInc = (endY - startY) / (seq.length + 1);
  171. startY += yInc / 2;
  172. for (ii in 0...seq.length) {
  173. centerOn(seq[ii], startX + ii * xInc, startY + ii * yInc);
  174. }
  175. }
  176. public static function distributeChildrenVertically (disp :DisplayObject, ?startTop :Float =
  177. 0, gap :Float = 0) :Void
  178. {
  179. if (!(Std.is( disp, DisplayObjectContainer))) {
  180. return;
  181. }
  182. var container = cast(disp, DisplayObjectContainer);
  183. var bounds:Rectangle;
  184. var child:DisplayObject;
  185. for (ii in 0...Std.int(container.numChildren)) {
  186. child = cast( container.getChildAt(ii), DisplayObject);
  187. setTopLeft(child, child.x, startTop);
  188. startTop += child.height + gap;
  189. }
  190. }
  191. public static function setTopLeft (disp :DisplayObject, x :Float = 0, y :Float = 0) :Void
  192. {
  193. if (disp.parent == null) {
  194. throw "Cannot place a DisplayObject without a parent";
  195. }
  196. #if (flash || spaceport)
  197. var bounds = disp.getBounds(disp.parent);
  198. #elseif cpp
  199. var bounds = disp.nmeGetPixelBounds();
  200. #end
  201. disp.x = x - bounds.left;
  202. disp.y = y - bounds.top;
  203. }
  204. public static function distributionPoint (index :Float, length :Float, startX :Float, startY :Float,
  205. endX :Float, endY :Float) :Point
  206. {
  207. var xInc = (endX - startX) / (length + 1);
  208. startX += xInc;
  209. var yInc = (endY - startY) / (length + 1);
  210. startY += yInc;
  211. return new Point(startX + index * xInc, startY + index * yInc);
  212. }
  213. public static function getBoundsCenter (d :DisplayObject) :Point
  214. {
  215. #if (flash || spaceport)
  216. var bounds = d.getBounds(d);
  217. #elseif cpp
  218. var bounds = d.nmeGetPixelBounds();
  219. #end
  220. return new Point(bounds.left + bounds.width / 2, bounds.top + bounds.height / 2);
  221. }
  222. public static function getBoundsCenterRelativeTo (d :DisplayObject,
  223. relativeTo :DisplayObject) :Point
  224. {
  225. #if (flash || spaceport)
  226. var bounds = d.getBounds(relativeTo);
  227. #elseif cpp
  228. var bounds = d.nmeGetPixelBounds();
  229. #end
  230. return new Point(bounds.left + bounds.width / 2, bounds.top + bounds.height / 2);
  231. }
  232. public static function getCenterOffset (d :DisplayObject) :Point
  233. {
  234. return getCenterOffsetRelativeTo(d, d);
  235. }
  236. public static function getCenterOffsetRelativeTo (d :DisplayObject,
  237. relativeTo :DisplayObject) :Point
  238. {
  239. #if (flash || spaceport)
  240. var bounds = d.getBounds(relativeTo);
  241. #elseif cpp
  242. var bounds = d.nmeGetPixelBounds();
  243. #end
  244. var centerX:Float = bounds.left + bounds.width / 2;
  245. var centerY:Float = bounds.top + bounds.height / 2;
  246. return new Point(centerX - d.x, centerY - d.y);
  247. }
  248. public static function getChildren (d :DisplayObjectContainer) :Array<Dynamic>
  249. {
  250. var children:Array<Dynamic> = [];
  251. if (d == null) {
  252. return children;
  253. }
  254. for (ii in 0...d.numChildren) {
  255. if (d.getChildAt(ii) != null) {
  256. children.push(d.getChildAt(ii));
  257. }
  258. }
  259. return children;
  260. }
  261. public static function getChildNamed (parent :DisplayObjectContainer, name :String) :DisplayObject
  262. {
  263. var namedChild :DisplayObject = null;
  264. var getname = function (d :DisplayObject) :Bool {
  265. if (d.name == name) {
  266. namedChild = d;
  267. return true;
  268. }
  269. return false;
  270. }
  271. org.transition9.util.DisplayUtils.applyToHierarchy(parent, getname);
  272. return namedChild;
  273. }
  274. public static function getGlobalScale (d :DisplayObject, ?currentScale :Float = 1) :Float
  275. {
  276. if (d == null || d.stage == null || d == d.stage) {
  277. return currentScale;
  278. } else {
  279. return getGlobalScale(d.parent, d.scaleX * currentScale);
  280. }
  281. }
  282. #if flash
  283. public static function loadBitmapFromUrl (url :String, ?loadedBitmapDataCallback :Dynamic =
  284. null) :Bitmap
  285. {
  286. if (url == null) {
  287. return null;
  288. }
  289. var bm:Bitmap = new Bitmap();
  290. try {
  291. var imageLoader = new Loader();
  292. var loaderContext = new flash.system.LoaderContext();
  293. loaderContext.checkPolicyFile = true;
  294. function () :Void {
  295. if (imageLoader.content != null && Std.is( imageLoader.content, DisplayObject)) {
  296. var bd:BitmapData = BitmapUtil.createBitmapData(cast( imageLoader.content, DisplayObject));
  297. bm.bitmapData = bd;
  298. if (loadedBitmapDataCallback != null) {
  299. loadedBitmapDataCallback(bd);
  300. }
  301. }
  302. }
  303. var request:URLRequest = new URLRequest(url);
  304. trace("Not implemented");
  305. // imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,
  306. // F.justOnce(F.callback_(onComplete)));
  307. // imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,
  308. // function (ignored:Array<Dynamic>) :Void {
  309. // Log.error("URL not found: " + url);
  310. // });
  311. imageLoader.load(request, loaderContext);
  312. } catch (err :IOErrorEvent) {
  313. Log.error("URL not found: " + url);
  314. }
  315. return bm;
  316. }
  317. /**
  318. * From a list of DisplayObjects on the stage, create a combined bitmap, with a
  319. * layer order of the supplied array.
  320. */
  321. public static function mergeDisplayObjects (dos :Array<Dynamic>, ?scale :Float = 1,
  322. ?preserveBounds :Bool = false) :Bitmap
  323. {
  324. if (dos == null || dos.length == 0) {
  325. return null;
  326. }
  327. // trace("mergeDisplayObjects, scale=" + scale);
  328. var bounds:Rectangle = null;
  329. var stageBounds:Rectangle;
  330. var disp:DisplayObject;
  331. var origins:Array<Dynamic> = [];
  332. var origin:Point;
  333. for (disp in dos) {
  334. stageBounds = disp.getBounds(disp.stage);
  335. origins.push(new Point(stageBounds.left, stageBounds.top));
  336. if (bounds != null) {
  337. bounds = bounds.union(stageBounds);
  338. } else {
  339. bounds = stageBounds;
  340. }
  341. }
  342. var bd:BitmapData = new BitmapData(cast(bounds.width * scale), cast(bounds.height * scale),
  343. true, 0xffffff);
  344. var topLeft:Point = new Point(bounds.left, bounds.top);
  345. for (ii in 0...dos.length) {
  346. disp = cast( dos[ii], DisplayObject);
  347. origin = cast( origins[ii], Point);
  348. var localBounds:Rectangle = disp.getBounds(disp);
  349. // bd.draw(disp, new Matrix(1, 0, 0, 1, -localBounds.left + (origin.x - topLeft.x),
  350. // -localBounds.top + (origin.y - topLeft.y)));
  351. bd.draw(disp, new Matrix(scale, 0, 0, scale,
  352. scale * (-localBounds.left + (origin.x - topLeft.x)),
  353. scale * (-localBounds.top + (origin.y - topLeft.y))));
  354. }
  355. var bm:Bitmap = new Bitmap(bd);
  356. return bm;
  357. }
  358. #end
  359. public static function placeSequence (parent :DisplayObjectContainer, seq :Array<Dynamic>, startX :Float,
  360. startY :Float, ?direction :Float = 0, ?gap :Float = 5, ?center :Bool = true) :Void
  361. {
  362. if (seq == null || seq.length == 0 || parent == null) {
  363. return;
  364. }
  365. for (d in seq) {
  366. if (d == null) {
  367. continue;
  368. }
  369. parent.addChild(d);
  370. var xAdjust:Float = 0;
  371. var yAdjust:Float = 0;
  372. if (center) {
  373. if (direction == LEFT_TO_RIGHT) {
  374. xAdjust = d.width / 2;
  375. } else if (direction == RIGHT_TO_LEFT) {
  376. xAdjust = -d.width / 2;
  377. } else if (direction == TOP_TO_BOTTOM) {
  378. yAdjust = d.height / 2;
  379. } else if (direction == BOTTOM_TO_TOP) {
  380. yAdjust = -d.height / 2;
  381. }
  382. centerOn(d, startX + xAdjust, startY + yAdjust);
  383. } else {
  384. d.x = startX + xAdjust;
  385. d.y = startY + yAdjust;
  386. }
  387. if (direction == LEFT_TO_RIGHT) {
  388. startX += d.width + gap;
  389. } else if (direction == RIGHT_TO_LEFT) {
  390. startX += -(d.width + gap);
  391. } else if (direction == TOP_TO_BOTTOM) {
  392. startY += d.height + gap;
  393. } else if (direction == BOTTOM_TO_TOP) {
  394. startY += -(d.height + gap);
  395. }
  396. }
  397. }
  398. public static function removeAllChildren (parent :DisplayObject) :Void
  399. {
  400. if (parent == null || !(Std.is(parent, DisplayObjectContainer))) {
  401. return;
  402. }
  403. while (cast(parent, DisplayObjectContainer).numChildren > 0) {
  404. cast(parent, DisplayObjectContainer).removeChildAt(0);
  405. }
  406. }
  407. /**
  408. * Creates a bitmap from the given DisplayObject, and positions the bitmap so that it is
  409. * visually in the same position as the argument.
  410. */
  411. #if flash
  412. public static function screenShot (disp :DisplayObject) :Bitmap
  413. {
  414. if (disp == null || disp.stage == null) {
  415. Log.error(["d", disp, "d.stage", disp == null ? null : disp.stage]);
  416. return null;
  417. }
  418. var scale:Float = getGlobalScale(disp, 1);
  419. var bd:BitmapData = new BitmapData(Std.int(disp.stage.stageWidth), Std.int(disp.stage.stageHeight), true,
  420. 0xffffff);
  421. var stageBounds = disp.getBounds(disp.stage);
  422. var origin:Point = new Point(stageBounds.left, stageBounds.top);
  423. var localBounds:Rectangle = disp.getBounds(disp);
  424. var topLeft:Point = new Point(0, 0);
  425. bd.draw(disp, new Matrix(scale, 0, 0, scale,
  426. scale * (-localBounds.left + (origin.x - topLeft.x)),
  427. scale * (-localBounds.top + (origin.y - topLeft.y))));
  428. var bm:Bitmap = new Bitmap(bd);
  429. return bm;
  430. }
  431. #end
  432. public static function shrinkAndCenterOn (disp :DisplayObject, ?maxSize :Float = 20) :DisplayObject
  433. {
  434. if (maxSize > 0) {
  435. var max = Math.max(disp.width, disp.height);
  436. if (max > maxSize) {
  437. disp.scaleX = disp.scaleY = maxSize / max;
  438. }
  439. }
  440. var s:Sprite = new Sprite();
  441. s.addChild(disp);
  442. DisplayUtils.centerOn(disp);
  443. return s;
  444. }
  445. /**
  446. * Creates a bitmap from the given DisplayObject, and positions the bitmap so that it is
  447. * visually in the same position as the argument. Optionally supply a resolution scale
  448. * factor, so if parent container is scaled, the Bitmap won't be pixellated.
  449. */
  450. #if flash
  451. public static function substituteBitmap (d :DisplayObject, ?resolutionFactor :Float = 1) :Bitmap
  452. {
  453. if (d == null) {
  454. return null;
  455. }
  456. var bm:Bitmap = convertToBitmap(d, resolutionFactor);
  457. if (bm == null) {
  458. Log.error(["d", d, "resolutionFactor", resolutionFactor, "bm", bm]);
  459. return null;
  460. }
  461. bm.scaleX = bm.scaleY = 1 / resolutionFactor;
  462. var bounds:Rectangle = d.getBounds(d);
  463. //Center it according to the offsets.
  464. bm.x = bounds.left;
  465. bm.y = bounds.top;
  466. return bm;
  467. }
  468. #end
  469. // #if (flash || cpp)
  470. // public static function createBitmapData (d :DisplayObject, ?scale :Float = 1.0, ?center :Point) :BitmapData
  471. // {
  472. // #if flash
  473. // var bounds = d.getBounds(d);
  474. // #elseif cpp
  475. // var bounds = d.nmeGetPixelBounds();
  476. // #end
  477. // if (bounds.width <= 0 && bounds.height <= 0) {
  478. // org.transition9.util.Log.error(["d", d, "d.name", d.name, "bounds", bounds]);
  479. // return null;
  480. // }
  481. // if (center != null) {
  482. // center.x = Mathematics.ceil(-bounds.x * scale);
  483. // center.y = Mathematics.ceil(-bounds.y * scale);
  484. // }
  485. // #if flash
  486. // var bd = new BitmapData(Mathematics.ceil(bounds.width * scale), Mathematics.ceil(bounds.height * scale), true, toARGB(0xffffff, 0));
  487. // #else
  488. // var bd = new BitmapData(Mathematics.ceil(bounds.width * scale), Mathematics.ceil(bounds.height * scale), true, 0xffffff);
  489. // #end
  490. // bd.draw(d, new Matrix(scale, 0, 0, scale, center.x, center.y), null, null, null, false);
  491. // #if graphics_debug
  492. // var shape = new flash.display.Shape();
  493. // var g = shape.graphics;
  494. // g.lineStyle(1, 0);
  495. // g.drawRect(0, 0, bd.width - 1, bd.height - 1);
  496. // bd.draw(shape);
  497. // #end
  498. // return bd;
  499. // }
  500. // public static function createBitmapDataCentered (d :DisplayObject, ?scale :Float = 1.0, ?center :Point) :BitmapData
  501. // {
  502. // #if flash
  503. // var bounds = d.getBounds(d);
  504. // #elseif cpp
  505. // var bounds = d.nmeGetPixelBounds();
  506. // #end
  507. // if (bounds.width <= 0 && bounds.height <= 0) {
  508. // org.transition9.util.Log.error(["d", d, "d.name", d.name, "bounds", bounds]);
  509. // return null;
  510. // }
  511. // var size = Math.max(bounds.width, bounds.height);
  512. // if (center != null) {
  513. // center.x = size / 2 * scale;
  514. // center.y = size / 2 * scale;
  515. // }
  516. // var bd = new BitmapData(Std.int(size * scale), Std.int(size * scale),
  517. // true, toARGB(0xffffff, 0));
  518. // // bd.draw(d, new Matrix(scale, 0, 0, scale, -bounds.left * scale, -bounds.top * scale), null, null, null, true);
  519. // bd.draw(d, new Matrix(scale, 0, 0, scale, size / 2 * scale, size / 2 * scale), null, null, null, true);
  520. // return bd;
  521. // }
  522. // #end
  523. }