Object.hx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. package h2d;
  2. import hxd.Math;
  3. /**
  4. h2d.Object is the base 2D class that all scene tree elements inherit from.
  5. It can be used to create a virtual container that does not display anything but can contain other objects
  6. so the various transforms are inherited to its children.
  7. **/
  8. @:allow(h2d.Tools)
  9. class Object {
  10. static var nullDrawable : h2d.Drawable;
  11. var children : Array<Object>;
  12. var parentContainer : Object;
  13. /**
  14. The parent object in the scene tree.
  15. **/
  16. public var parent(default, null) : Object;
  17. /**
  18. How many immediate children this object has.
  19. **/
  20. public var numChildren(get, never) : Int;
  21. /**
  22. The name of the object, can be used to retrieve an object within a tree by using `getObjectByName` (default null)
  23. **/
  24. public var name : String;
  25. /**
  26. The x position (in pixels) of the object relative to its parent.
  27. **/
  28. public var x(default,set) : Float;
  29. /**
  30. The y position (in pixels) of the object relative to its parent.
  31. **/
  32. public var y(default, set) : Float;
  33. /**
  34. The amount of horizontal scaling of this object (default 1.0)
  35. **/
  36. public var scaleX(default,set) : Float;
  37. /**
  38. The amount of vertical scaling of this object (default 1.0)
  39. **/
  40. public var scaleY(default,set) : Float;
  41. /**
  42. The rotation angle of this object, in radians.
  43. **/
  44. public var rotation(default, set) : Float;
  45. /**
  46. Is the object and its children are displayed on screen (default true).
  47. **/
  48. public var visible(default, set) : Bool;
  49. /**
  50. The amount of transparency of the Object (default 1.0)
  51. **/
  52. public var alpha : Float = 1.;
  53. /**
  54. The post process filter for this object.
  55. **/
  56. public var filter(default,set) : h2d.filter.Filter;
  57. var matA : Float;
  58. var matB : Float;
  59. var matC : Float;
  60. var matD : Float;
  61. var absX : Float;
  62. var absY : Float;
  63. var posChanged : Bool;
  64. var allocated : Bool;
  65. var lastFrame : Int;
  66. /**
  67. Create a new empty object, and adds it to the parent object if not null.
  68. **/
  69. public function new( ?parent : Object ) {
  70. matA = 1; matB = 0; matC = 0; matD = 1; absX = 0; absY = 0;
  71. x = 0; y = 0; scaleX = 1; scaleY = 1; rotation = 0;
  72. posChanged = parent != null;
  73. visible = true;
  74. children = [];
  75. if( parent != null )
  76. parent.addChild(this);
  77. }
  78. /**
  79. Return the bounds of the object for its whole content, recursively.
  80. If relativeTo is null, it will return the bounds in the absolute coordinates.
  81. If not, it will return the bounds relative to the specified object coordinates.
  82. You can pass an already allocated bounds or getBounds will allocate one for you and return it.
  83. **/
  84. public function getBounds( ?relativeTo : Object, ?out : h2d.col.Bounds ) : h2d.col.Bounds {
  85. if( out == null ) out = new h2d.col.Bounds() else out.empty();
  86. if( relativeTo != null )
  87. relativeTo.syncPos();
  88. if( relativeTo != this )
  89. syncPos();
  90. getBoundsRec(relativeTo, out, false);
  91. if( out.isEmpty() ) {
  92. addBounds(relativeTo, out, -1, -1, 2, 2);
  93. out.xMax = out.xMin = (out.xMax + out.xMin) * 0.5;
  94. out.yMax = out.yMin = (out.yMax + out.yMin) * 0.5;
  95. }
  96. return out;
  97. }
  98. /**
  99. Similar to getBounds(parent), but instead of the full content, it will return
  100. the size based on the alignement of the object. For instance for a text, getBounds will return
  101. the full glyphs size whereas getSize() will ignore the pixels under the baseline.
  102. **/
  103. public function getSize( ?out : h2d.col.Bounds ) : h2d.col.Bounds {
  104. if( out == null ) out = new h2d.col.Bounds() else out.empty();
  105. syncPos();
  106. getBoundsRec(parent, out, true);
  107. if( out.isEmpty() ) {
  108. addBounds(parent, out, -1, -1, 2, 2);
  109. out.xMax = out.xMin = (out.xMax + out.xMin) * 0.5;
  110. out.yMax = out.yMin = (out.yMax + out.yMin) * 0.5;
  111. }
  112. out.offset( -x, -y);
  113. return out;
  114. }
  115. /**
  116. Find a single object in the tree by calling `f` on each and returning the first not-null value returned, or null if not found.
  117. **/
  118. public function find<T>( f : Object -> Null<T> ) : Null<T> {
  119. var v = f(this);
  120. if( v != null )
  121. return v;
  122. for( o in children ) {
  123. var v = o.find(f);
  124. if( v != null ) return v;
  125. }
  126. return null;
  127. }
  128. /**
  129. Find several objects in the tree by calling `f` on each and returning all the not-null values returned.
  130. **/
  131. public function findAll<T>( f : Object -> Null<T>, ?arr : Array<T> ) : Array<T> {
  132. if( arr == null ) arr = [];
  133. var v = f(this);
  134. if( v != null )
  135. arr.push(v);
  136. for( o in children )
  137. o.findAll(f,arr);
  138. return arr;
  139. }
  140. function set_filter(f : h2d.filter.Filter) {
  141. if( filter != null && allocated ) filter.unbind(this);
  142. filter = f;
  143. if( f != null && allocated ) f.bind(this);
  144. return f;
  145. }
  146. function getBoundsRec( relativeTo : Object, out : h2d.col.Bounds, forSize : Bool ) : Void {
  147. if( posChanged ) {
  148. calcAbsPos();
  149. for( c in children )
  150. c.posChanged = true;
  151. posChanged = false;
  152. }
  153. var n = children.length;
  154. if( n == 0 ) {
  155. out.empty();
  156. return;
  157. }
  158. if( n == 1 ) {
  159. var c = children[0];
  160. if( c.visible ) c.getBoundsRec(relativeTo, out,forSize) else out.empty();
  161. return;
  162. }
  163. var xmin = hxd.Math.POSITIVE_INFINITY, ymin = hxd.Math.POSITIVE_INFINITY;
  164. var xmax = hxd.Math.NEGATIVE_INFINITY, ymax = hxd.Math.NEGATIVE_INFINITY;
  165. for( c in children ) {
  166. if( !c.visible ) continue;
  167. c.getBoundsRec(relativeTo, out, forSize);
  168. if( out.xMin < xmin ) xmin = out.xMin;
  169. if( out.yMin < ymin ) ymin = out.yMin;
  170. if( out.xMax > xmax ) xmax = out.xMax;
  171. if( out.yMax > ymax ) ymax = out.yMax;
  172. }
  173. out.xMin = xmin;
  174. out.yMin = ymin;
  175. out.xMax = xmax;
  176. out.yMax = ymax;
  177. }
  178. function addBounds( relativeTo : Object, out : h2d.col.Bounds, dx : Float, dy : Float, width : Float, height : Float ) {
  179. if( width <= 0 || height <= 0 ) return;
  180. if( relativeTo == null ) {
  181. var x, y;
  182. out.addPos(dx * matA + dy * matC + absX, dx * matB + dy * matD + absY);
  183. out.addPos((dx + width) * matA + dy * matC + absX, (dx + width) * matB + dy * matD + absY);
  184. out.addPos(dx * matA + (dy + height) * matC + absX, dx * matB + (dy + height) * matD + absY);
  185. out.addPos((dx + width) * matA + (dy + height) * matC + absX, (dx + width) * matB + (dy + height) * matD + absY);
  186. return;
  187. }
  188. if( relativeTo == this ) {
  189. if( out.xMin > dx ) out.xMin = dx;
  190. if( out.yMin > dy ) out.yMin = dy;
  191. if( out.xMax < dx + width ) out.xMax = dx + width;
  192. if( out.yMax < dy + height ) out.yMax = dy + height;
  193. return;
  194. }
  195. var r = relativeTo.matA * relativeTo.matD - relativeTo.matB * relativeTo.matC;
  196. if( r == 0 )
  197. return;
  198. var det = 1 / r;
  199. var rA = relativeTo.matD * det;
  200. var rB = -relativeTo.matB * det;
  201. var rC = -relativeTo.matC * det;
  202. var rD = relativeTo.matA * det;
  203. var rX = absX - relativeTo.absX;
  204. var rY = absY - relativeTo.absY;
  205. var x, y;
  206. x = dx * matA + dy * matC + rX;
  207. y = dx * matB + dy * matD + rY;
  208. out.addPos(x * rA + y * rC, x * rB + y * rD);
  209. x = (dx + width) * matA + dy * matC + rX;
  210. y = (dx + width) * matB + dy * matD + rY;
  211. out.addPos(x * rA + y * rC, x * rB + y * rD);
  212. x = dx * matA + (dy + height) * matC + rX;
  213. y = dx * matB + (dy + height) * matD + rY;
  214. out.addPos(x * rA + y * rC, x * rB + y * rD);
  215. x = (dx + width) * matA + (dy + height) * matC + rX;
  216. y = (dx + width) * matB + (dy + height) * matD + rY;
  217. out.addPos(x * rA + y * rC, x * rB + y * rD);
  218. }
  219. /**
  220. Return the total number of children, recursively.
  221. **/
  222. public function getObjectsCount() : Int {
  223. var k = 0;
  224. for( c in children )
  225. k += c.getObjectsCount() + 1;
  226. return k;
  227. }
  228. /**
  229. Convert a local position (or [0,0] if pt is null) relative to the object origin into an absolute screen position, applying all the inherited transforms.
  230. **/
  231. public function localToGlobal( ?pt : h2d.col.Point ) : h2d.col.Point {
  232. syncPos();
  233. if( pt == null ) pt = new h2d.col.Point();
  234. var px = pt.x * matA + pt.y * matC + absX;
  235. var py = pt.x * matB + pt.y * matD + absY;
  236. pt.x = px;
  237. pt.y = py;
  238. return pt;
  239. }
  240. /**
  241. Convert an absolute screen position into a local position relative to the object origin, applying all the inherited transforms.
  242. **/
  243. public function globalToLocal( pt : h2d.col.Point ) : h2d.col.Point {
  244. syncPos();
  245. pt.x -= absX;
  246. pt.y -= absY;
  247. var invDet = 1 / (matA * matD - matB * matC);
  248. var px = (pt.x * matD - pt.y * matC) * invDet;
  249. var py = (-pt.x * matB + pt.y * matA) * invDet;
  250. pt.x = px;
  251. pt.y = py;
  252. return pt;
  253. }
  254. function getScene() : Scene {
  255. var p = this;
  256. while( p.parent != null ) p = p.parent;
  257. return Std.instance(p, Scene);
  258. }
  259. function set_visible(b) {
  260. if( visible == b )
  261. return b;
  262. visible = b;
  263. onContentChanged();
  264. return b;
  265. }
  266. /**
  267. Add a child object at the end of the children list.
  268. **/
  269. public function addChild( s : Object ) : Void {
  270. addChildAt(s, children.length);
  271. }
  272. /**
  273. Insert a child object at the specified position of the children list.
  274. **/
  275. public function addChildAt( s : Object, pos : Int ) : Void {
  276. if( pos < 0 ) pos = 0;
  277. if( pos > children.length ) pos = children.length;
  278. var p = this;
  279. while( p != null ) {
  280. if( p == s ) throw "Recursive addChild";
  281. p = p.parent;
  282. }
  283. if( s.parent != null ) {
  284. // prevent calling onRemove
  285. var old = s.allocated;
  286. s.allocated = false;
  287. s.parent.removeChild(s);
  288. s.allocated = old;
  289. }
  290. children.insert(pos, s);
  291. if( !allocated && s.allocated )
  292. s.onRemove();
  293. s.parent = this;
  294. s.parentContainer = parentContainer;
  295. s.posChanged = true;
  296. // ensure that proper alloc/delete is done if we change parent
  297. if( allocated ) {
  298. if( !s.allocated )
  299. s.onAdd();
  300. else
  301. s.onParentChanged();
  302. }
  303. onContentChanged();
  304. }
  305. inline function onContentChanged() {
  306. if( parentContainer != null ) parentContainer.contentChanged(this);
  307. }
  308. // called when we're allocated already but moved in hierarchy
  309. function onParentChanged() {
  310. for( c in children )
  311. c.onParentChanged();
  312. }
  313. // kept for internal init
  314. function onAdd() {
  315. allocated = true;
  316. if( filter != null )
  317. filter.bind(this);
  318. for( c in children )
  319. c.onAdd();
  320. }
  321. // kept for internal cleanup
  322. function onRemove() {
  323. allocated = false;
  324. if( filter != null )
  325. filter.unbind(this);
  326. for( c in children )
  327. c.onRemove();
  328. }
  329. function getMatrix( m : h2d.col.Matrix ) {
  330. m.a = matA;
  331. m.b = matB;
  332. m.c = matC;
  333. m.d = matD;
  334. m.x = absX;
  335. m.y = absY;
  336. }
  337. /**
  338. Remove the given object from our immediate children list if it's part of it.
  339. **/
  340. public function removeChild( s : Object ) {
  341. if( children.remove(s) ) {
  342. if( s.allocated ) s.onRemove();
  343. s.parent = null;
  344. if( s.parentContainer != null ) s.setParentContainer(null);
  345. s.posChanged = true;
  346. onContentChanged();
  347. }
  348. }
  349. function setParentContainer( c : Object ) {
  350. parentContainer = c;
  351. for( s in children )
  352. s.setParentContainer(c);
  353. }
  354. /**
  355. Remove all children from our immediate children list
  356. **/
  357. public function removeChildren() {
  358. while( numChildren>0 )
  359. removeChild( getChildAt(0) );
  360. }
  361. /**
  362. Same as parent.removeChild(this), but does nothing if parent is null.
  363. In order to capture add/removal from scene, you can override onAdd/onRemove/onParentChanged
  364. **/
  365. public inline function remove() {
  366. if( this != null && parent != null ) parent.removeChild(this);
  367. }
  368. /**
  369. Draw the object and all its children into the given Texture
  370. **/
  371. public function drawTo( t : h3d.mat.Texture ) {
  372. var s = getScene();
  373. var needDispose = s == null;
  374. if( s == null ) s = new h2d.Scene();
  375. @:privateAccess s.drawImplTo(this, t);
  376. if( needDispose ) s.dispose();
  377. }
  378. function draw( ctx : RenderContext ) {
  379. }
  380. function sync( ctx : RenderContext ) {
  381. var changed = posChanged;
  382. if( changed ) {
  383. calcAbsPos();
  384. posChanged = false;
  385. }
  386. lastFrame = ctx.frame;
  387. var p = 0, len = children.length;
  388. while( p < len ) {
  389. var c = children[p];
  390. if( c == null )
  391. break;
  392. if( c.lastFrame != ctx.frame ) {
  393. if( changed ) c.posChanged = true;
  394. c.sync(ctx);
  395. }
  396. // if the object was removed, let's restart again.
  397. // our lastFrame ensure that no object will get synched twice
  398. if( children[p] != c ) {
  399. p = 0;
  400. len = children.length;
  401. } else
  402. p++;
  403. }
  404. }
  405. function syncPos() {
  406. if( parent != null ) parent.syncPos();
  407. if( posChanged ) {
  408. calcAbsPos();
  409. for( c in children )
  410. c.posChanged = true;
  411. posChanged = false;
  412. }
  413. }
  414. function calcAbsPos() {
  415. if( parent == null ) {
  416. var cr, sr;
  417. if( rotation == 0 ) {
  418. cr = 1.; sr = 0.;
  419. matA = scaleX;
  420. matB = 0;
  421. matC = 0;
  422. matD = scaleY;
  423. } else {
  424. cr = Math.cos(rotation);
  425. sr = Math.sin(rotation);
  426. matA = scaleX * cr;
  427. matB = scaleX * sr;
  428. matC = scaleY * -sr;
  429. matD = scaleY * cr;
  430. }
  431. absX = x;
  432. absY = y;
  433. } else {
  434. // M(rel) = S . R . T
  435. // M(abs) = M(rel) . P(abs)
  436. if( rotation == 0 ) {
  437. matA = scaleX * parent.matA;
  438. matB = scaleX * parent.matB;
  439. matC = scaleY * parent.matC;
  440. matD = scaleY * parent.matD;
  441. } else {
  442. var cr = Math.cos(rotation);
  443. var sr = Math.sin(rotation);
  444. var tmpA = scaleX * cr;
  445. var tmpB = scaleX * sr;
  446. var tmpC = scaleY * -sr;
  447. var tmpD = scaleY * cr;
  448. matA = tmpA * parent.matA + tmpB * parent.matC;
  449. matB = tmpA * parent.matB + tmpB * parent.matD;
  450. matC = tmpC * parent.matA + tmpD * parent.matC;
  451. matD = tmpC * parent.matB + tmpD * parent.matD;
  452. }
  453. absX = x * parent.matA + y * parent.matC + parent.absX;
  454. absY = x * parent.matB + y * parent.matD + parent.absY;
  455. }
  456. }
  457. function emitTile( ctx : RenderContext, tile : h2d.Tile ) {
  458. if( nullDrawable == null )
  459. nullDrawable = @:privateAccess new h2d.Drawable(null);
  460. if( !ctx.hasBuffering() ) {
  461. nullDrawable.absX = absX;
  462. nullDrawable.absY = absY;
  463. nullDrawable.matA = matA;
  464. nullDrawable.matB = matB;
  465. nullDrawable.matC = matC;
  466. nullDrawable.matD = matD;
  467. ctx.drawTile(nullDrawable, tile);
  468. return;
  469. }
  470. if( !ctx.beginDrawBatch(nullDrawable, tile.getTexture()) ) return;
  471. var ax = absX + tile.dx * matA + tile.dy * matC;
  472. var ay = absY + tile.dx * matB + tile.dy * matD;
  473. var buf = ctx.buffer;
  474. var pos = ctx.bufPos;
  475. buf.grow(pos + 4 * 8);
  476. inline function emit(v:Float) buf[pos++] = v;
  477. emit(ax);
  478. emit(ay);
  479. emit(tile.u);
  480. emit(tile.v);
  481. emit(1.);
  482. emit(1.);
  483. emit(1.);
  484. emit(ctx.globalAlpha);
  485. var tw = tile.width;
  486. var th = tile.height;
  487. var dx1 = tw * matA;
  488. var dy1 = tw * matB;
  489. var dx2 = th * matC;
  490. var dy2 = th * matD;
  491. emit(ax + dx1);
  492. emit(ay + dy1);
  493. emit(tile.u2);
  494. emit(tile.v);
  495. emit(1.);
  496. emit(1.);
  497. emit(1.);
  498. emit(ctx.globalAlpha);
  499. emit(ax + dx2);
  500. emit(ay + dy2);
  501. emit(tile.u);
  502. emit(tile.v2);
  503. emit(1.);
  504. emit(1.);
  505. emit(1.);
  506. emit(ctx.globalAlpha);
  507. emit(ax + dx1 + dx2);
  508. emit(ay + dy1 + dy2);
  509. emit(tile.u2);
  510. emit(tile.v2);
  511. emit(1.);
  512. emit(1.);
  513. emit(1.);
  514. emit(ctx.globalAlpha);
  515. ctx.bufPos = pos;
  516. }
  517. /**
  518. Clip a local bounds with our global viewport
  519. **/
  520. function clipBounds( ctx : RenderContext, bounds : h2d.col.Bounds ) {
  521. var view = ctx.tmpBounds;
  522. var matA, matB, matC, matD, absX, absY;
  523. @:privateAccess if( ctx.inFilter != null ) {
  524. var f1 = ctx.baseShader.filterMatrixA;
  525. var f2 = ctx.baseShader.filterMatrixB;
  526. matA = this.matA * f1.x + this.matB * f1.y;
  527. matB = this.matA * f2.x + this.matB * f2.y;
  528. matC = this.matC * f1.x + this.matD * f1.y;
  529. matD = this.matC * f2.x + this.matD * f2.y;
  530. absX = this.absX * f1.x + this.absY * f1.y + f1.z;
  531. absY = this.absX * f2.x + this.absY * f2.y + f2.z;
  532. } else {
  533. matA = this.matA;
  534. matB = this.matB;
  535. matC = this.matC;
  536. matD = this.matD;
  537. absX = this.absX;
  538. absY = this.absY;
  539. }
  540. // intersect our transformed local view with our viewport in global space
  541. view.empty();
  542. inline function add(x:Float, y:Float) {
  543. view.addPos(x * matA + y * matC + absX, x * matB + y * matD + absY);
  544. }
  545. add(bounds.xMin, bounds.yMin);
  546. add(bounds.xMax, bounds.yMin);
  547. add(bounds.xMin, bounds.yMax);
  548. add(bounds.xMax, bounds.yMax);
  549. // clip with our scene
  550. @:privateAccess {
  551. if( view.xMin < ctx.curX ) view.xMin = ctx.curX;
  552. if( view.yMin < ctx.curY ) view.yMin = ctx.curY;
  553. if( view.xMax > ctx.curX + ctx.curWidth ) view.xMax = ctx.curX + ctx.curWidth;
  554. if( view.yMax > ctx.curY + ctx.curHeight ) view.yMax = ctx.curY + ctx.curHeight;
  555. }
  556. // inverse our matrix
  557. var invDet = 1 / (matA * matD - matB * matC);
  558. inline function add(x:Float, y:Float) {
  559. x -= absX;
  560. y -= absY;
  561. view.addPos((x * matD - y * matC) * invDet, ( -x * matB + y * matA) * invDet);
  562. }
  563. // intersect our resulting viewport with our calculated local space
  564. var sxMin = view.xMin;
  565. var syMin = view.yMin;
  566. var sxMax = view.xMax;
  567. var syMax = view.yMax;
  568. view.empty();
  569. add(sxMin, syMin);
  570. add(sxMax, syMin);
  571. add(sxMin, syMax);
  572. add(sxMax, syMax);
  573. // intersects
  574. bounds.doIntersect(view);
  575. }
  576. function drawFilters( ctx : RenderContext ) {
  577. if( !ctx.pushFilter(this) ) return;
  578. var bounds = ctx.tmpBounds;
  579. var total = new h2d.col.Bounds();
  580. var maxExtent = -1.;
  581. filter.sync(ctx, this);
  582. if( filter.autoBounds ) {
  583. maxExtent = filter.boundsExtend;
  584. } else {
  585. filter.getBounds(this, bounds);
  586. total.addBounds(bounds);
  587. }
  588. if( maxExtent >= 0 ) {
  589. getBounds(this, bounds);
  590. bounds.xMin -= maxExtent;
  591. bounds.yMin -= maxExtent;
  592. bounds.xMax += maxExtent;
  593. bounds.yMax += maxExtent;
  594. total.addBounds(bounds);
  595. }
  596. clipBounds(ctx, total);
  597. var xMin = Math.floor(total.xMin + 1e-10);
  598. var yMin = Math.floor(total.yMin + 1e-10);
  599. var width = Math.ceil(total.xMax - xMin - 1e-10);
  600. var height = Math.ceil(total.yMax - yMin - 1e-10);
  601. if( width <= 0 || height <= 0 || total.xMax < total.xMin ) return;
  602. var t = ctx.textures.allocTarget("filterTemp", width, height, false);
  603. ctx.pushTarget(t, xMin, yMin, width, height);
  604. ctx.engine.clear(0);
  605. // reset transform and update children
  606. var oldAlpha = ctx.globalAlpha;
  607. var shader = @:privateAccess ctx.baseShader;
  608. var oldA = shader.filterMatrixA.clone();
  609. var oldB = shader.filterMatrixB.clone();
  610. var oldF = @:privateAccess ctx.inFilter;
  611. // 2x3 inverse matrix
  612. var invDet = 1 / (matA * matD - matB * matC);
  613. var invA = matD * invDet;
  614. var invB = -matB * invDet;
  615. var invC = -matC * invDet;
  616. var invD = matA * invDet;
  617. var invX = -(absX * invA + absY * invC);
  618. var invY = -(absX * invB + absY * invD);
  619. shader.filterMatrixA.set(invA, invC, invX);
  620. shader.filterMatrixB.set(invB, invD, invY);
  621. ctx.globalAlpha = 1;
  622. draw(ctx);
  623. for( c in children )
  624. c.drawRec(ctx);
  625. ctx.flush();
  626. var finalTile = h2d.Tile.fromTexture(t);
  627. finalTile.dx = xMin;
  628. finalTile.dy = yMin;
  629. var prev = finalTile;
  630. finalTile = filter.draw(ctx, finalTile);
  631. if( finalTile != prev && finalTile != null ) {
  632. finalTile.dx += xMin;
  633. finalTile.dy += yMin;
  634. }
  635. shader.filterMatrixA.load(oldA);
  636. shader.filterMatrixB.load(oldB);
  637. ctx.popTarget();
  638. ctx.popFilter();
  639. if( finalTile == null )
  640. return;
  641. ctx.globalAlpha = oldAlpha * alpha;
  642. emitTile(ctx, finalTile);
  643. ctx.globalAlpha = oldAlpha;
  644. ctx.flush();
  645. }
  646. function drawRec( ctx : RenderContext ) {
  647. if( !visible ) return;
  648. // fallback in case the object was added during a sync() event and we somehow didn't update it
  649. if( posChanged ) {
  650. // only sync anim, don't update() (prevent any event from occuring during draw())
  651. // if( currentAnimation != null ) currentAnimation.sync();
  652. calcAbsPos();
  653. for( c in children )
  654. c.posChanged = true;
  655. posChanged = false;
  656. }
  657. if( filter != null ) {
  658. drawFilters(ctx);
  659. } else {
  660. var old = ctx.globalAlpha;
  661. ctx.globalAlpha *= alpha;
  662. if( ctx.front2back ) {
  663. var nchilds = children.length;
  664. for (i in 0...nchilds) children[nchilds - 1 - i].drawRec(ctx);
  665. draw(ctx);
  666. } else {
  667. draw(ctx);
  668. for( c in children ) c.drawRec(ctx);
  669. }
  670. ctx.globalAlpha = old;
  671. }
  672. }
  673. inline function set_x(v) {
  674. posChanged = true;
  675. return x = v;
  676. }
  677. inline function set_y(v) {
  678. posChanged = true;
  679. return y = v;
  680. }
  681. inline function set_scaleX(v) {
  682. posChanged = true;
  683. return scaleX = v;
  684. }
  685. inline function set_scaleY(v) {
  686. posChanged = true;
  687. return scaleY = v;
  688. }
  689. inline function set_rotation(v) {
  690. posChanged = true;
  691. return rotation = v;
  692. }
  693. /**
  694. Move the object by the specied amount along its current direction (rotation angle).
  695. **/
  696. public function move( dx : Float, dy : Float ) {
  697. x += dx * Math.cos(rotation);
  698. y += dy * Math.sin(rotation);
  699. }
  700. /**
  701. Set the position of the object relative to its parent.
  702. **/
  703. public inline function setPosition( x : Float, y : Float ) {
  704. this.x = x;
  705. this.y = y;
  706. }
  707. /**
  708. Rotate the object by the given angle (in radians)
  709. **/
  710. public inline function rotate( v : Float ) {
  711. rotation += v;
  712. }
  713. /**
  714. Scale uniformly the object by the given factor.
  715. **/
  716. public inline function scale( v : Float ) {
  717. scaleX *= v;
  718. scaleY *= v;
  719. }
  720. /**
  721. Set the uniform scale for the object.
  722. **/
  723. public inline function setScale( v : Float ) {
  724. scaleX = v;
  725. scaleY = v;
  726. }
  727. /**
  728. Return the `n`th element among our immediate children list, or null if there is no.
  729. **/
  730. public inline function getChildAt( n ) {
  731. return children[n];
  732. }
  733. /**
  734. Return the index of the object `o` within our immediate children list, or `-1` if it is not part of our children list.
  735. **/
  736. public function getChildIndex( o ) {
  737. for( i in 0...children.length )
  738. if( children[i] == o )
  739. return i;
  740. return -1;
  741. }
  742. /**
  743. Search for an object recursively by name, return null if not found.
  744. **/
  745. public function getObjectByName( name : String ) {
  746. if( this.name == name )
  747. return this;
  748. for( c in children ) {
  749. var o = c.getObjectByName(name);
  750. if( o != null ) return o;
  751. }
  752. return null;
  753. }
  754. inline function get_numChildren() {
  755. return children.length;
  756. }
  757. /**
  758. Return an iterator over this object immediate children
  759. **/
  760. public inline function iterator() {
  761. return new hxd.impl.ArrayIterator(children);
  762. }
  763. function toString() {
  764. var c = Type.getClassName(Type.getClass(this));
  765. return name == null ? c : name + "(" + c + ")";
  766. }
  767. // ---- additional methods for containers (h2d.Flow)
  768. /**
  769. This is called by our children if we have defined their parentContainer when they get resized
  770. **/
  771. function contentChanged( s : Object ) {
  772. }
  773. /**
  774. This can be called by a parent container to constraint the size of its children.
  775. Negative value mean that constraint is to be disable.
  776. **/
  777. function constraintSize( maxWidth : Float, maxHeight : Float ) {
  778. }
  779. }