Graphics.hx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. package h2d;
  2. import h2d.impl.BatchDrawState;
  3. import hxd.Math;
  4. import hxd.impl.Allocator;
  5. private typedef GraphicsPoint = hxd.poly2tri.Point;
  6. @:dox(hide)
  7. class GPoint {
  8. public var x : Float;
  9. public var y : Float;
  10. public var r : Float;
  11. public var g : Float;
  12. public var b : Float;
  13. public var a : Float;
  14. public function new() {
  15. }
  16. public function load(x, y, r, g, b, a ){
  17. this.x = x;
  18. this.y = y;
  19. this.r = r;
  20. this.g = g;
  21. this.b = b;
  22. this.a = a;
  23. }
  24. }
  25. private class GraphicsContent extends h3d.prim.Primitive {
  26. var tmp : hxd.FloatBuffer;
  27. var index : hxd.IndexBuffer;
  28. var state : BatchDrawState;
  29. var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes, state : BatchDrawState }>;
  30. var bufferDirty : Bool;
  31. var indexDirty : Bool;
  32. var allocPos : hxd.impl.AllocPos;
  33. public function new() {
  34. buffers = [];
  35. state = new BatchDrawState();
  36. this.allocPos = hxd.impl.AllocPos.make();
  37. }
  38. public inline function addIndex(i) {
  39. index.push(i);
  40. state.add(1);
  41. indexDirty = true;
  42. }
  43. public inline function add( x : Float, y : Float, u : Float, v : Float, r : Float, g : Float, b : Float, a : Float ) {
  44. tmp.push(x);
  45. tmp.push(y);
  46. tmp.push(u);
  47. tmp.push(v);
  48. tmp.push(r);
  49. tmp.push(g);
  50. tmp.push(b);
  51. tmp.push(a);
  52. bufferDirty = true;
  53. }
  54. public function setTile( tile : h2d.Tile ) {
  55. state.setTile(tile);
  56. }
  57. public function next() {
  58. var nvect = tmp.length >> 3;
  59. if( nvect < 1 << 15 )
  60. return false;
  61. buffers.push( { buf : tmp, idx : index, vbuf : null, ibuf : null, state: state } );
  62. tmp = new hxd.FloatBuffer();
  63. index = new hxd.IndexBuffer();
  64. var tex = state.currentTexture;
  65. state = new BatchDrawState();
  66. state.setTexture(tex);
  67. super.dispose();
  68. return true;
  69. }
  70. override function alloc( engine : h3d.Engine ) {
  71. if (index.length <= 0) return ;
  72. var alloc = Allocator.get();
  73. buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D);
  74. @:privateAccess buffer.allocPos = allocPos;
  75. indexes = alloc.ofIndexes(index);
  76. for( b in buffers ) {
  77. if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, hxd.BufferFormat.H2D);
  78. if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx);
  79. }
  80. bufferDirty = false;
  81. indexDirty = false;
  82. }
  83. public function doRender( ctx : h2d.RenderContext ) {
  84. if ( index.length == 0 ) return;
  85. flush();
  86. for ( b in buffers ) b.state.drawIndexed(ctx, b.vbuf, b.ibuf);
  87. state.drawIndexed(ctx, buffer, indexes);
  88. }
  89. public function flush() {
  90. if( buffer == null || buffer.isDisposed() ) {
  91. alloc(h3d.Engine.getCurrent());
  92. } else {
  93. var allocator = Allocator.get();
  94. if ( bufferDirty ) {
  95. allocator.disposeBuffer(buffer);
  96. buffer = allocator.ofFloats(tmp, hxd.BufferFormat.H2D);
  97. bufferDirty = false;
  98. }
  99. if ( indexDirty ) {
  100. allocator.disposeIndexBuffer(indexes);
  101. indexes = allocator.ofIndexes(index);
  102. indexDirty = false;
  103. }
  104. }
  105. }
  106. override function dispose() {
  107. for( b in buffers ) {
  108. if( b.vbuf != null ) Allocator.get().disposeBuffer(b.vbuf);
  109. if( b.ibuf != null ) Allocator.get().disposeIndexBuffer(b.ibuf);
  110. b.vbuf = null;
  111. b.ibuf = null;
  112. b.state.clear();
  113. }
  114. if( buffer != null ) {
  115. Allocator.get().disposeBuffer(buffer);
  116. buffer = null;
  117. }
  118. if( indexes != null ) {
  119. Allocator.get().disposeIndexBuffer(indexes);
  120. indexes = null;
  121. }
  122. state.clear();
  123. super.dispose();
  124. }
  125. public function clear() {
  126. dispose();
  127. tmp = new hxd.FloatBuffer();
  128. index = new hxd.IndexBuffer();
  129. buffers = [];
  130. }
  131. }
  132. /**
  133. A simple interface to draw arbitrary 2D geometry.
  134. Usage notes:
  135. * While Graphics allows for multiple unique textures, each texture swap causes a new drawcall,
  136. and due to that it's recommended to minimize the amount of used textures per Graphics instance,
  137. ideally limiting to only one texture.
  138. * Due to how Graphics operate, removing them from the active `h2d.Scene` will cause a loss of all data.
  139. **/
  140. class Graphics extends Drawable {
  141. var content : GraphicsContent;
  142. var tmpPoints : Array<GPoint>;
  143. var pindex : Int;
  144. var curR : Float;
  145. var curG : Float;
  146. var curB : Float;
  147. var curA : Float;
  148. var lineSize : Float;
  149. var lineR : Float;
  150. var lineG : Float;
  151. var lineB : Float;
  152. var lineA : Float;
  153. var doFill : Bool;
  154. var xMin : Float;
  155. var yMin : Float;
  156. var xMax : Float;
  157. var yMax : Float;
  158. var xMinSize : Float;
  159. var yMinSize : Float;
  160. var xMaxSize : Float;
  161. var yMaxSize : Float;
  162. var ma : Float = 1.;
  163. var mb : Float = 0.;
  164. var mc : Float = 0.;
  165. var md : Float = 1.;
  166. var mx : Float = 0.;
  167. var my : Float = 0.;
  168. /**
  169. The Tile used as source of Texture to render.
  170. **/
  171. public var tile : h2d.Tile;
  172. /**
  173. Adds bevel cut-off at line corners.
  174. The value is a percentile in range of 0...1, dictating at which point edges get beveled based on their angle.
  175. Value of 0 being not beveled and 1 being always beveled.
  176. **/
  177. public var bevel = 0.25; //0 = not beveled, 1 = always beveled
  178. /**
  179. Create a new Graphics instance.
  180. @param parent An optional parent `h2d.Object` instance to which Graphics adds itself if set.
  181. **/
  182. public function new(?parent) {
  183. super(parent);
  184. content = new GraphicsContent();
  185. tile = h2d.Tile.fromColor(0xFFFFFF);
  186. clear();
  187. }
  188. override function onRemove() {
  189. super.onRemove();
  190. clear();
  191. }
  192. /**
  193. Clears the Graphics contents.
  194. **/
  195. public function clear() {
  196. content.clear();
  197. tmpPoints = [];
  198. pindex = 0;
  199. lineSize = 0;
  200. xMin = Math.POSITIVE_INFINITY;
  201. yMin = Math.POSITIVE_INFINITY;
  202. yMax = Math.NEGATIVE_INFINITY;
  203. xMax = Math.NEGATIVE_INFINITY;
  204. xMinSize = Math.POSITIVE_INFINITY;
  205. yMinSize = Math.POSITIVE_INFINITY;
  206. yMaxSize = Math.NEGATIVE_INFINITY;
  207. xMaxSize = Math.NEGATIVE_INFINITY;
  208. }
  209. override function getBoundsRec( relativeTo, out, forSize ) {
  210. super.getBoundsRec(relativeTo, out, forSize);
  211. if( tile != null ) {
  212. if( forSize ) addBounds(relativeTo, out, xMinSize, yMinSize, xMaxSize - xMinSize, yMaxSize - yMinSize);
  213. else addBounds(relativeTo, out, xMin, yMin, xMax - xMin, yMax - yMin);
  214. }
  215. }
  216. function isConvex( points : Array<GPoint> ) {
  217. var first = true, sign = false;
  218. for( i in 0...points.length ) {
  219. var p1 = points[i];
  220. var p2 = points[(i + 1) % points.length];
  221. var p3 = points[(i + 2) % points.length];
  222. var s = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0;
  223. if( first ) {
  224. first = false;
  225. sign = s;
  226. } else if( sign != s )
  227. return false;
  228. }
  229. return true;
  230. }
  231. function flushLine( start ) {
  232. var pts = tmpPoints;
  233. var last = pts.length - 1;
  234. var prev = pts[last];
  235. var p = pts[0];
  236. content.setTile(h2d.Tile.fromColor(0xFFFFFF));
  237. var closed = p.x == prev.x && p.y == prev.y;
  238. var count = pts.length;
  239. if( !closed ) {
  240. var prevLast = pts[last - 1];
  241. if( prevLast == null ) prevLast = p;
  242. var gp = new GPoint();
  243. gp.load(prev.x * 2 - prevLast.x, prev.y * 2 - prevLast.y, 0, 0, 0, 0);
  244. pts.push(gp);
  245. var pNext = pts[1];
  246. if( pNext == null ) pNext = p;
  247. var gp = new GPoint();
  248. gp.load(p.x * 2 - pNext.x, p.y * 2 - pNext.y, 0, 0, 0, 0);
  249. prev = gp;
  250. } else if( p != prev ) {
  251. count--;
  252. last--;
  253. prev = pts[last];
  254. }
  255. for( i in 0...count ) {
  256. var next = pts[(i + 1) % pts.length];
  257. var nx1 = prev.y - p.y;
  258. var ny1 = p.x - prev.x;
  259. var ns1 = Math.invSqrt(nx1 * nx1 + ny1 * ny1);
  260. var nx2 = p.y - next.y;
  261. var ny2 = next.x - p.x;
  262. var ns2 = Math.invSqrt(nx2 * nx2 + ny2 * ny2);
  263. var nx = nx1 * ns1 + nx2 * ns2;
  264. var ny = ny1 * ns1 + ny2 * ns2;
  265. var ns = Math.invSqrt(nx * nx + ny * ny);
  266. nx *= ns;
  267. ny *= ns;
  268. var size = nx * nx1 * ns1 + ny * ny1 * ns1; // N.N1
  269. // *HACK* we should instead properly detect limits when the angle is too small
  270. if(size < 0.1) size = 0.1;
  271. var d = lineSize * 0.5 / size;
  272. nx *= d;
  273. ny *= d;
  274. if(size > bevel) {
  275. content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
  276. content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
  277. var pnext = i == last ? start : pindex + 2;
  278. if( i < count - 1 || closed ) {
  279. content.addIndex(pindex);
  280. content.addIndex(pindex + 1);
  281. content.addIndex(pnext);
  282. content.addIndex(pindex + 1);
  283. content.addIndex(pnext);
  284. content.addIndex(pnext + 1);
  285. }
  286. pindex += 2;
  287. }
  288. else {
  289. //bevel
  290. var n0x = next.x - p.x;
  291. var n0y = next.y - p.y;
  292. var sign = n0x * nx + n0y * ny;
  293. var nnx = -ny;
  294. var nny = nx;
  295. var size = nnx * nx1 * ns1 + nny * ny1 * ns1;
  296. var d = lineSize * 0.5 / size;
  297. nnx *= d;
  298. nny *= d;
  299. var pnext = i == last ? start : pindex + 3;
  300. if(sign > 0) {
  301. content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
  302. content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a);
  303. content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a);
  304. content.addIndex(pindex);
  305. content.addIndex(pnext);
  306. content.addIndex(pindex + 2);
  307. content.addIndex(pindex + 2);
  308. content.addIndex(pnext);
  309. content.addIndex(pnext + 1);
  310. }
  311. else {
  312. content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a);
  313. content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
  314. content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a);
  315. content.addIndex(pindex + 1);
  316. content.addIndex(pnext);
  317. content.addIndex(pindex + 2);
  318. content.addIndex(pindex + 1);
  319. content.addIndex(pnext);
  320. content.addIndex(pnext + 1);
  321. }
  322. content.addIndex(pindex);
  323. content.addIndex(pindex + 1);
  324. content.addIndex(pindex + 2);
  325. pindex += 3;
  326. }
  327. prev = p;
  328. p = next;
  329. }
  330. content.setTile(tile);
  331. }
  332. static var EARCUT = null;
  333. function flushFill( i0 ) {
  334. if( tmpPoints.length < 3 )
  335. return;
  336. var pts = tmpPoints;
  337. var p0 = pts[0];
  338. var p1 = pts[pts.length - 1];
  339. var last = null;
  340. // closed poly
  341. if( hxd.Math.abs(p0.x - p1.x) < 1e-9 && hxd.Math.abs(p0.y - p1.y) < 1e-9 )
  342. last = pts.pop();
  343. if( isConvex(pts) ) {
  344. for( i in 1...pts.length - 1 ) {
  345. content.addIndex(i0);
  346. content.addIndex(i0 + i);
  347. content.addIndex(i0 + i + 1);
  348. }
  349. } else {
  350. var ear = EARCUT;
  351. if( ear == null )
  352. EARCUT = ear = new hxd.earcut.Earcut();
  353. for( i in ear.triangulate(pts) )
  354. content.addIndex(i + i0);
  355. }
  356. if( last != null )
  357. pts.push(last);
  358. }
  359. function flush() {
  360. if( tmpPoints.length == 0 )
  361. return;
  362. if( doFill ) {
  363. flushFill(pindex);
  364. pindex += tmpPoints.length;
  365. if( content.next() )
  366. pindex = 0;
  367. }
  368. if( lineSize > 0 ) {
  369. flushLine(pindex);
  370. if( content.next() )
  371. pindex = 0;
  372. }
  373. tmpPoints = [];
  374. }
  375. /**
  376. Begins a solid color fill.
  377. Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`.
  378. @param color An RGB color with which to fill the drawn shapes.
  379. @param alpha A transparency of the fill color.
  380. **/
  381. public function beginFill( color : Int = 0, alpha = 1. ) {
  382. flush();
  383. tile = h2d.Tile.fromColor(0xFFFFFF);
  384. content.setTile(tile);
  385. setColor(color,alpha);
  386. doFill = true;
  387. }
  388. /**
  389. Position a virtual tile at the given position and scale. Every draw will display a part of this tile relative
  390. to these coordinates.
  391. Note that in by default, Tile is not wrapped, and in order to render a tiling texture, `Drawable.tileWrap` has to be set.
  392. Additionally, both `Tile.dx` and `Tile.dy` are ignored (use `dx`/`dy` arguments instead)
  393. as well as tile defined size of the tile through `Tile.width` and `Tile.height` (use `scaleX`/`scaleY` relative to texture size).
  394. Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`.
  395. @param dx An X offset of the Tile relative to Graphics.
  396. @param dy An Y offset of the Tile relative to Graphics.
  397. @param scaleX A horizontal scale factor applied to the Tile texture.
  398. @param scaleY A vertical scale factor applied to the Tile texture.
  399. @param tile The tile to fill with. If null, uses previously used Tile with `beginTileFill` or throws an error.
  400. Previous tile is remembered across `Graphics.clear` calls.
  401. **/
  402. public function beginTileFill( ?dx : Float, ?dy : Float, ?scaleX : Float, ?scaleY : Float, ?tile : h2d.Tile ) {
  403. if ( tile == null )
  404. tile = this.tile;
  405. if ( tile == null )
  406. throw "Tile not specified";
  407. flush();
  408. this.tile = tile;
  409. content.setTile(tile);
  410. setColor(0xFFFFFF);
  411. doFill = true;
  412. if( dx == null ) dx = 0;
  413. if( dy == null ) dy = 0;
  414. if( scaleX == null ) scaleX = 1;
  415. if( scaleY == null ) scaleY = 1;
  416. dx -= tile.x;
  417. dy -= tile.y;
  418. var tex = tile.getTexture();
  419. var pixWidth = 1 / tex.width;
  420. var pixHeight = 1 / tex.height;
  421. ma = pixWidth / scaleX;
  422. mb = 0;
  423. mc = 0;
  424. md = pixHeight / scaleY;
  425. mx = -dx * ma;
  426. my = -dy * md;
  427. }
  428. /**
  429. Draws a Tile at given position.
  430. See `Graphics.beginTileFill` for limitations.
  431. This methods ends current fill operation.
  432. @param x The X position of the tile.
  433. @param y The Y position of the tile.
  434. @param tile The tile to draw.
  435. **/
  436. public function drawTile( x : Float, y : Float, tile : h2d.Tile ) {
  437. beginTileFill(x, y, tile);
  438. drawRect(x, y, tile.width, tile.height);
  439. endFill();
  440. }
  441. /**
  442. Sets an outline style. Changing the line style ends the currently drawn line.
  443. @param size Width of the outline. Setting size to 0 will remove the outline.
  444. @param color An outline RGB color.
  445. @param alpha An outline transparency.
  446. **/
  447. public function lineStyle( size : Float = 0, color = 0, alpha = 1. ) {
  448. flush();
  449. this.lineSize = size;
  450. lineA = alpha;
  451. lineR = ((color >> 16) & 0xFF) / 255.;
  452. lineG = ((color >> 8) & 0xFF) / 255.;
  453. lineB = (color & 0xFF) / 255.;
  454. }
  455. /**
  456. Ends the current line and starts new one at given position.
  457. **/
  458. public inline function moveTo(x,y) {
  459. flush();
  460. lineTo(x, y);
  461. }
  462. /**
  463. Ends the current fill operation.
  464. **/
  465. public function endFill() {
  466. flush();
  467. doFill = false;
  468. }
  469. /**
  470. Changes current fill color.
  471. Does not interrupt current fill operation and can be utilized to customize color per vertex.
  472. During tile fill operation, color serves as a tile color multiplier.
  473. @param color The new fill color.
  474. @param alpha The new fill transparency.
  475. **/
  476. public inline function setColor( color : Int, alpha : Float = 1. ) {
  477. curA = alpha;
  478. curR = ((color >> 16) & 0xFF) / 255.;
  479. curG = ((color >> 8) & 0xFF) / 255.;
  480. curB = (color & 0xFF) / 255.;
  481. }
  482. /**
  483. Draws a rectangle with given parameters.
  484. @param x The rectangle top-left corner X position.
  485. @param y The rectangle top-left corner Y position.
  486. @param w The rectangle width.
  487. @param h The rectangle height.
  488. **/
  489. public function drawRect( x : Float, y : Float, w : Float, h : Float ) {
  490. flush();
  491. lineTo(x, y);
  492. lineTo(x + w, y);
  493. lineTo(x + w, y + h);
  494. lineTo(x, y + h);
  495. lineTo(x, y);
  496. var e = 0.01; // see #776
  497. tmpPoints[0].x += e;
  498. tmpPoints[0].y += e;
  499. tmpPoints[1].y += e;
  500. tmpPoints[3].x += e;
  501. tmpPoints[4].x += e;
  502. tmpPoints[4].y += e;
  503. flush();
  504. }
  505. /**
  506. Draws a rounded rectangle with given parameters.
  507. @param x The rectangle top-left corner X position.
  508. @param y The rectangle top-left corner Y position.
  509. @param w The rectangle width.
  510. @param h The rectangle height.
  511. @param radius Radius of the rectangle corners.
  512. @param nsegments Amount of segments used for corners. When `0` segment count calculated automatically.
  513. **/
  514. public function drawRoundedRect( x : Float, y : Float, w : Float, h : Float, radius : Float, nsegments = 0 ) {
  515. if (radius <= 0) {
  516. return drawRect(x, y, w, h);
  517. }
  518. x += radius;
  519. y += radius;
  520. w -= radius * 2;
  521. h -= radius * 2;
  522. flush();
  523. if( nsegments == 0 )
  524. nsegments = Math.ceil(Math.abs(radius * hxd.Math.degToRad(90) / 4));
  525. if( nsegments < 3 ) nsegments = 3;
  526. var angle = hxd.Math.degToRad(90) / (nsegments - 1);
  527. inline function corner(x, y, angleStart) {
  528. for ( i in 0...nsegments) {
  529. var a = i * angle + hxd.Math.degToRad(angleStart);
  530. lineTo(x + Math.cos(a) * radius, y + Math.sin(a) * radius);
  531. }
  532. }
  533. lineTo(x, y - radius);
  534. lineTo(x + w, y - radius);
  535. corner(x + w, y, 270);
  536. lineTo(x + w + radius, y + h);
  537. corner(x + w, y + h, 0);
  538. lineTo(x, y + h + radius);
  539. corner(x, y + h, 90);
  540. lineTo(x - radius, y);
  541. corner(x, y, 180);
  542. flush();
  543. }
  544. /**
  545. Draws a circle centered at given position.
  546. @param cx X center position of the circle.
  547. @param cy Y center position of the circle.
  548. @param radius Radius of the circle.
  549. @param nsegments Amount of segments used to draw the circle. When `0`, amount of segments calculated automatically.
  550. **/
  551. public function drawCircle( cx : Float, cy : Float, radius : Float, nsegments = 0 ) {
  552. flush();
  553. if( nsegments == 0 )
  554. nsegments = Math.ceil(Math.abs(radius * 3.14 * 2 / 4));
  555. if( nsegments < 3 ) nsegments = 3;
  556. var angle = Math.PI * 2 / nsegments;
  557. for( i in 0...nsegments + 1 ) {
  558. var a = i * angle;
  559. lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
  560. }
  561. flush();
  562. }
  563. /**
  564. Draws an ellipse centered at given position.
  565. @param cx X center position of the ellipse.
  566. @param cy Y center position of the ellipse.
  567. @param radiusX Horizontal radius of an ellipse.
  568. @param radiusY Vertical radius of an ellipse.
  569. @param rotationAngle Ellipse rotation in radians.
  570. @param nsegments Amount of segments used to draw an ellipse. When `0`, amount of segments calculated automatically.
  571. **/
  572. public function drawEllipse( cx : Float, cy : Float, radiusX : Float, radiusY : Float, rotationAngle : Float = 0, nsegments = 0 ) {
  573. flush();
  574. if( nsegments == 0 )
  575. nsegments = Math.ceil(Math.abs(radiusY * 3.14 * 2 / 4));
  576. if( nsegments < 3 ) nsegments = 3;
  577. var angle = Math.PI * 2 / nsegments;
  578. var x1, y1;
  579. for( i in 0...nsegments + 1 ) {
  580. var a = i * angle;
  581. x1 = Math.cos(a) * Math.cos(rotationAngle) * radiusX - Math.sin(a) * Math.sin(rotationAngle) * radiusY;
  582. y1 = Math.cos(rotationAngle) * Math.sin(a) * radiusY + Math.cos(a) * Math.sin(rotationAngle) * radiusX;
  583. lineTo(cx + x1, cy + y1);
  584. }
  585. flush();
  586. }
  587. /**
  588. Draws a pie centered at given position.
  589. @param cx X center position of the pie.
  590. @param cy Y center position of the pie.
  591. @param radius Radius of the pie.
  592. @param angleStart Starting angle of the pie in radians.
  593. @param angleLength The pie size in clockwise direction with `2*PI` being full circle.
  594. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
  595. **/
  596. public function drawPie( cx : Float, cy : Float, radius : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) {
  597. if(Math.abs(angleLength) >= Math.PI * 2) {
  598. return drawCircle(cx, cy, radius, nsegments);
  599. }
  600. flush();
  601. lineTo(cx, cy);
  602. if( nsegments == 0 )
  603. nsegments = Math.ceil(Math.abs(radius * angleLength / 4));
  604. if( nsegments < 3 ) nsegments = 3;
  605. var angle = angleLength / (nsegments - 1);
  606. for( i in 0...nsegments ) {
  607. var a = i * angle + angleStart;
  608. lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
  609. }
  610. lineTo(cx, cy);
  611. flush();
  612. }
  613. /**
  614. Draws a double-edged pie centered at given position.
  615. @param cx X center position of the pie.
  616. @param cy Y center position of the pie.
  617. @param radius The outer radius of the pie.
  618. @param innerRadius The inner radius of the pie.
  619. @param angleStart Starting angle of the pie in radians.
  620. @param angleLength The pie size in clockwise direction with `2*PI` being full circle.
  621. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
  622. **/
  623. public function drawPieInner( cx : Float, cy : Float, radius : Float, innerRadius : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) {
  624. flush();
  625. if( Math.abs(angleLength) >= Math.PI * 2 + 1e-3 ) angleLength = Math.PI*2+1e-3;
  626. var cs = Math.cos(angleStart);
  627. var ss = Math.sin(angleStart);
  628. var ce = Math.cos(angleStart + angleLength);
  629. var se = Math.sin(angleStart + angleLength);
  630. lineTo(cx + cs * innerRadius, cy + ss * innerRadius);
  631. if( nsegments == 0 )
  632. nsegments = Math.ceil(Math.abs(radius * angleLength / 4));
  633. if( nsegments < 3 ) nsegments = 3;
  634. var angle = angleLength / (nsegments - 1);
  635. for( i in 0...nsegments ) {
  636. var a = i * angle + angleStart;
  637. lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
  638. }
  639. lineTo(cx + ce * innerRadius, cy + se * innerRadius);
  640. for( i in 0...nsegments ) {
  641. var a = (nsegments - 1 - i) * angle + angleStart;
  642. lineTo(cx + Math.cos(a) * innerRadius, cy + Math.sin(a) * innerRadius);
  643. }
  644. flush();
  645. }
  646. /**
  647. Draws a rectangular pie centered at given position.
  648. @param cx X center position of the pie.
  649. @param cy Y center position of the pie.
  650. @param width Width of the pie.
  651. @param height Height of the pie.
  652. @param angleStart Starting angle of the pie in radians.
  653. @param angleLength The pie size in clockwise direction with `2*PI` being solid rectangle.
  654. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
  655. **/
  656. public function drawRectanglePie( cx : Float, cy : Float, width : Float, height : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) {
  657. if(Math.abs(angleLength) >= Math.PI*2) {
  658. return drawRect(cx-(width/2), cy-(height/2), width, height);
  659. }
  660. flush();
  661. lineTo(cx, cy);
  662. if( nsegments == 0 )
  663. nsegments = Math.ceil(Math.abs(Math.max(width, height) * angleLength / 4));
  664. if( nsegments < 3 ) nsegments = 3;
  665. var angle = angleLength / (nsegments - 1);
  666. var square2 = Math.sqrt(2);
  667. for( i in 0...nsegments ) {
  668. var a = i * angle + angleStart;
  669. var _width = Math.cos(a) * (width/2+1) * square2;
  670. var _height = Math.sin(a) * (height/2+1) * square2;
  671. _width = Math.abs(_width) >= width/2 ? (Math.cos(a) < 0 ? width/2*-1 : width/2) : _width;
  672. _height = Math.abs(_height) >= height/2 ? (Math.sin(a) < 0 ? height/2*-1 : height/2) : _height;
  673. lineTo(cx + _width, cy + _height);
  674. }
  675. lineTo(cx, cy);
  676. flush();
  677. }
  678. /**
  679. * Draws a quadratic Bezier curve using the current line style from the current drawing position to (cx, cy) and using the control point that (bx, by) specifies.
  680. * IvanK Lib port ( http://lib.ivank.net )
  681. */
  682. public function curveTo( bx : Float, by : Float, cx : Float, cy : Float) {
  683. var ax = tmpPoints.length == 0 ? 0 :tmpPoints[ tmpPoints.length - 1 ].x;
  684. var ay = tmpPoints.length == 0 ? 0 :tmpPoints[ tmpPoints.length - 1 ].y;
  685. var t = 2 / 3;
  686. cubicCurveTo(ax + t * (bx - ax), ay + t * (by - ay), cx + t * (bx - cx), cy + t * (by - cy), cx, cy);
  687. }
  688. /**
  689. * Draws a cubic Bezier curve from the current drawing position to the specified anchor point.
  690. * IvanK Lib port ( http://lib.ivank.net )
  691. * @param bx control X for start point
  692. * @param by control Y for start point
  693. * @param cx control X for end point
  694. * @param cy control Y for end point
  695. * @param dx end X
  696. * @param dy end Y
  697. * @param nsegments = 40
  698. */
  699. public function cubicCurveTo( bx : Float, by : Float, cx : Float, cy : Float, dx : Float, dy : Float, nsegments = 40) {
  700. var ax = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].x;
  701. var ay = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].y;
  702. var tobx = bx - ax, toby = by - ay;
  703. var tocx = cx - bx, tocy = cy - by;
  704. var todx = dx - cx, tody = dy - cy;
  705. var step = 1 / nsegments;
  706. for (i in 1...nsegments) {
  707. var d = i * step;
  708. var px = ax + d * tobx, py = ay + d * toby;
  709. var qx = bx + d * tocx, qy = by + d * tocy;
  710. var rx = cx + d * todx, ry = cy + d * tody;
  711. var toqx = qx - px, toqy = qy - py;
  712. var torx = rx - qx, tory = ry - qy;
  713. var sx = px + d * toqx, sy = py + d * toqy;
  714. var tx = qx + d * torx, ty = qy + d * tory;
  715. var totx = tx - sx, toty = ty - sy;
  716. lineTo(sx + d * totx, sy + d * toty);
  717. }
  718. lineTo(dx, dy);
  719. }
  720. /**
  721. Draws a straight line from the current drawing position to the given position.
  722. **/
  723. public inline function lineTo( x : Float, y : Float ) {
  724. addVertex(x, y, curR, curG, curB, curA, x * ma + y * mc + mx, x * mb + y * md + my);
  725. }
  726. /**
  727. Advanced usage. Adds new vertex to the current polygon with given parameters and current line style.
  728. @param x Vertex X position
  729. @param y Vertex Y position
  730. @param r Red tint value of the vertex when performing fill operation.
  731. @param g Green tint value of the vertex when performing fill operation.
  732. @param b Blue tint value of the vertex when performing fill operation.
  733. @param a Alpha of the vertex when performing fill operation.
  734. @param u Normalized horizontal Texture position from the current Tile fill operation.
  735. @param v Normalized vertical Texture position from the current Tile fill operation.
  736. **/
  737. public function addVertex( x : Float, y : Float, r : Float, g : Float, b : Float, a : Float, u : Float = 0., v : Float = 0. ) {
  738. var half = lineSize / 2.0;
  739. if( x - half < xMin ) xMin = x - half;
  740. if( y - half < yMin ) yMin = y - half;
  741. if( x + half > xMax ) xMax = x + half;
  742. if( y + half > yMax ) yMax = y + half;
  743. if( x < xMinSize ) xMinSize = x;
  744. if( y < yMinSize ) yMinSize = y;
  745. if( x > xMaxSize ) xMaxSize = x;
  746. if( y > yMaxSize ) yMaxSize = y;
  747. if( doFill )
  748. content.add(x, y, u, v, r, g, b, a);
  749. var gp = new GPoint();
  750. gp.load(x, y, lineR, lineG, lineB, lineA);
  751. tmpPoints.push(gp);
  752. }
  753. override function draw(ctx:RenderContext) {
  754. if( !ctx.beginDrawBatchState(this) ) return;
  755. content.doRender(ctx);
  756. }
  757. override function sync(ctx:RenderContext) {
  758. super.sync(ctx);
  759. flush();
  760. content.flush();
  761. }
  762. }