package h2d; import h2d.impl.BatchDrawState; import hxd.Math; import hxd.impl.Allocator; private typedef GraphicsPoint = hxd.poly2tri.Point; @:dox(hide) class GPoint { public var x : Float; public var y : Float; public var r : Float; public var g : Float; public var b : Float; public var a : Float; public function new() { } public function load(x, y, r, g, b, a ){ this.x = x; this.y = y; this.r = r; this.g = g; this.b = b; this.a = a; } } private class GraphicsContent extends h3d.prim.Primitive { var tmp : hxd.FloatBuffer; var index : hxd.IndexBuffer; var state : BatchDrawState; var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes, state : BatchDrawState }>; var bufferDirty : Bool; var indexDirty : Bool; #if track_alloc var allocPos : hxd.impl.AllocPos; #end public function new() { buffers = []; state = new BatchDrawState(); #if track_alloc this.allocPos = new hxd.impl.AllocPos(); #end } public inline function addIndex(i) { index.push(i); state.add(1); indexDirty = true; } public inline function add( x : Float, y : Float, u : Float, v : Float, r : Float, g : Float, b : Float, a : Float ) { tmp.push(x); tmp.push(y); tmp.push(u); tmp.push(v); tmp.push(r); tmp.push(g); tmp.push(b); tmp.push(a); bufferDirty = true; } public function setTile( tile : h2d.Tile ) { state.setTile(tile); } public function next() { var nvect = tmp.length >> 3; if( nvect < 1 << 15 ) return false; buffers.push( { buf : tmp, idx : index, vbuf : null, ibuf : null, state: state } ); tmp = new hxd.FloatBuffer(); index = new hxd.IndexBuffer(); var tex = state.currentTexture; state = new BatchDrawState(); state.setTexture(tex); super.dispose(); return true; } override function alloc( engine : h3d.Engine ) { if (index.length <= 0) return ; var alloc = Allocator.get(); buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D); #if track_alloc @:privateAccess buffer.allocPos = allocPos; #end indexes = alloc.ofIndexes(index); for( b in buffers ) { if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, hxd.BufferFormat.H2D); if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx); } bufferDirty = false; indexDirty = false; } public function doRender( ctx : h2d.RenderContext ) { if ( index.length == 0 ) return; flush(); for ( b in buffers ) b.state.drawIndexed(ctx, b.vbuf, b.ibuf); state.drawIndexed(ctx, buffer, indexes); } public function flush() { if( buffer == null || buffer.isDisposed() ) { alloc(h3d.Engine.getCurrent()); } else { var allocator = Allocator.get(); if ( bufferDirty ) { allocator.disposeBuffer(buffer); buffer = allocator.ofFloats(tmp, hxd.BufferFormat.H2D); bufferDirty = false; } if ( indexDirty ) { allocator.disposeIndexBuffer(indexes); indexes = allocator.ofIndexes(index); indexDirty = false; } } } override function dispose() { for( b in buffers ) { if( b.vbuf != null ) Allocator.get().disposeBuffer(b.vbuf); if( b.ibuf != null ) Allocator.get().disposeIndexBuffer(b.ibuf); b.vbuf = null; b.ibuf = null; b.state.clear(); } if( buffer != null ) { Allocator.get().disposeBuffer(buffer); buffer = null; } if( indexes != null ) { Allocator.get().disposeIndexBuffer(indexes); indexes = null; } state.clear(); super.dispose(); } public function clear() { dispose(); tmp = new hxd.FloatBuffer(); index = new hxd.IndexBuffer(); buffers = []; } } /** A simple interface to draw arbitrary 2D geometry. Usage notes: * While Graphics allows for multiple unique textures, each texture swap causes a new drawcall, and due to that it's recommended to minimize the amount of used textures per Graphics instance, ideally limiting to only one texture. * Due to how Graphics operate, removing them from the active `h2d.Scene` will cause a loss of all data. **/ class Graphics extends Drawable { var content : GraphicsContent; var tmpPoints : Array; var pindex : Int; var curR : Float; var curG : Float; var curB : Float; var curA : Float; var lineSize : Float; var lineR : Float; var lineG : Float; var lineB : Float; var lineA : Float; var doFill : Bool; var xMin : Float; var yMin : Float; var xMax : Float; var yMax : Float; var xMinSize : Float; var yMinSize : Float; var xMaxSize : Float; var yMaxSize : Float; var ma : Float = 1.; var mb : Float = 0.; var mc : Float = 0.; var md : Float = 1.; var mx : Float = 0.; var my : Float = 0.; /** The Tile used as source of Texture to render. **/ public var tile : h2d.Tile; /** Adds bevel cut-off at line corners. The value is a percentile in range of 0...1, dictating at which point edges get beveled based on their angle. Value of 0 being not beveled and 1 being always beveled. **/ public var bevel = 0.25; //0 = not beveled, 1 = always beveled /** Create a new Graphics instance. @param parent An optional parent `h2d.Object` instance to which Graphics adds itself if set. **/ public function new(?parent) { super(parent); content = new GraphicsContent(); tile = h2d.Tile.fromColor(0xFFFFFF); clear(); } override function onRemove() { super.onRemove(); clear(); } /** Clears the Graphics contents. **/ public function clear() { content.clear(); tmpPoints = []; pindex = 0; lineSize = 0; xMin = Math.POSITIVE_INFINITY; yMin = Math.POSITIVE_INFINITY; yMax = Math.NEGATIVE_INFINITY; xMax = Math.NEGATIVE_INFINITY; xMinSize = Math.POSITIVE_INFINITY; yMinSize = Math.POSITIVE_INFINITY; yMaxSize = Math.NEGATIVE_INFINITY; xMaxSize = Math.NEGATIVE_INFINITY; } override function getBoundsRec( relativeTo, out, forSize ) { super.getBoundsRec(relativeTo, out, forSize); if( tile != null ) { if( forSize ) addBounds(relativeTo, out, xMinSize, yMinSize, xMaxSize - xMinSize, yMaxSize - yMinSize); else addBounds(relativeTo, out, xMin, yMin, xMax - xMin, yMax - yMin); } } function isConvex( points : Array ) { var first = true, sign = false; for( i in 0...points.length ) { var p1 = points[i]; var p2 = points[(i + 1) % points.length]; var p3 = points[(i + 2) % points.length]; var s = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0; if( first ) { first = false; sign = s; } else if( sign != s ) return false; } return true; } function flushLine( start ) { var pts = tmpPoints; var last = pts.length - 1; var prev = pts[last]; var p = pts[0]; content.setTile(h2d.Tile.fromColor(0xFFFFFF)); var closed = p.x == prev.x && p.y == prev.y; var count = pts.length; if( !closed ) { var prevLast = pts[last - 1]; if( prevLast == null ) prevLast = p; var gp = new GPoint(); gp.load(prev.x * 2 - prevLast.x, prev.y * 2 - prevLast.y, 0, 0, 0, 0); pts.push(gp); var pNext = pts[1]; if( pNext == null ) pNext = p; var gp = new GPoint(); gp.load(p.x * 2 - pNext.x, p.y * 2 - pNext.y, 0, 0, 0, 0); prev = gp; } else if( p != prev ) { count--; last--; prev = pts[last]; } for( i in 0...count ) { var next = pts[(i + 1) % pts.length]; var nx1 = prev.y - p.y; var ny1 = p.x - prev.x; var ns1 = Math.invSqrt(nx1 * nx1 + ny1 * ny1); var nx2 = p.y - next.y; var ny2 = next.x - p.x; var ns2 = Math.invSqrt(nx2 * nx2 + ny2 * ny2); var nx = nx1 * ns1 + nx2 * ns2; var ny = ny1 * ns1 + ny2 * ns2; var ns = Math.invSqrt(nx * nx + ny * ny); nx *= ns; ny *= ns; var size = nx * nx1 * ns1 + ny * ny1 * ns1; // N.N1 // *HACK* we should instead properly detect limits when the angle is too small if(size < 0.1) size = 0.1; var d = lineSize * 0.5 / size; nx *= d; ny *= d; if(size > bevel) { content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a); content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a); var pnext = i == last ? start : pindex + 2; if( i < count - 1 || closed ) { content.addIndex(pindex); content.addIndex(pindex + 1); content.addIndex(pnext); content.addIndex(pindex + 1); content.addIndex(pnext); content.addIndex(pnext + 1); } pindex += 2; } else { //bevel var n0x = next.x - p.x; var n0y = next.y - p.y; var sign = n0x * nx + n0y * ny; var nnx = -ny; var nny = nx; var size = nnx * nx1 * ns1 + nny * ny1 * ns1; var d = lineSize * 0.5 / size; nnx *= d; nny *= d; var pnext = i == last ? start : pindex + 3; if(sign > 0) { content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a); content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a); content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a); content.addIndex(pindex); content.addIndex(pnext); content.addIndex(pindex + 2); content.addIndex(pindex + 2); content.addIndex(pnext); content.addIndex(pnext + 1); } else { content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a); content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a); content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a); content.addIndex(pindex + 1); content.addIndex(pnext); content.addIndex(pindex + 2); content.addIndex(pindex + 1); content.addIndex(pnext); content.addIndex(pnext + 1); } content.addIndex(pindex); content.addIndex(pindex + 1); content.addIndex(pindex + 2); pindex += 3; } prev = p; p = next; } content.setTile(tile); } static var EARCUT = null; function flushFill( i0 ) { if( tmpPoints.length < 3 ) return; var pts = tmpPoints; var p0 = pts[0]; var p1 = pts[pts.length - 1]; var last = null; // closed poly if( hxd.Math.abs(p0.x - p1.x) < 1e-9 && hxd.Math.abs(p0.y - p1.y) < 1e-9 ) last = pts.pop(); if( isConvex(pts) ) { for( i in 1...pts.length - 1 ) { content.addIndex(i0); content.addIndex(i0 + i); content.addIndex(i0 + i + 1); } } else { var ear = EARCUT; if( ear == null ) EARCUT = ear = new hxd.earcut.Earcut(); for( i in ear.triangulate(pts) ) content.addIndex(i + i0); } if( last != null ) pts.push(last); } function flush() { if( tmpPoints.length == 0 ) return; if( doFill ) { flushFill(pindex); pindex += tmpPoints.length; if( content.next() ) pindex = 0; } if( lineSize > 0 ) { flushLine(pindex); if( content.next() ) pindex = 0; } tmpPoints = []; } /** Begins a solid color fill. Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`. @param color An RGB color with which to fill the drawn shapes. @param alpha A transparency of the fill color. **/ public function beginFill( color : Int = 0, alpha = 1. ) { flush(); tile = h2d.Tile.fromColor(0xFFFFFF); content.setTile(tile); setColor(color,alpha); doFill = true; } /** Position a virtual tile at the given position and scale. Every draw will display a part of this tile relative to these coordinates. Note that in by default, Tile is not wrapped, and in order to render tiling texture, `Drawable.tileWrap` have to be set. Additionally, both `Tile.dx` and `Tile.dy` are ignored (use `dx`/`dy` arguments instead) as well as tile defined size of the tile through `Tile.width` and `Tile.height` (use `scaleX`/`scaleY` relative to texture size). Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`. @param dx An X offset of the Tile relative to Graphics. @param dy An Y offset of the Tile relative to Graphics. @param scaleX A horizontal scale factor applied to the Tile texture. @param scaleY A vertical scale factor applied to the Tile texture. @param tile The tile to fill with. If null, uses previously used Tile with `beginTileFill` or throws an error. Previous tile is remembered across `Graphics.clear` calls. **/ public function beginTileFill( ?dx : Float, ?dy : Float, ?scaleX : Float, ?scaleY : Float, ?tile : h2d.Tile ) { if ( tile == null ) tile = this.tile; if ( tile == null ) throw "Tile not specified"; flush(); this.tile = tile; content.setTile(tile); setColor(0xFFFFFF); doFill = true; if( dx == null ) dx = 0; if( dy == null ) dy = 0; if( scaleX == null ) scaleX = 1; if( scaleY == null ) scaleY = 1; dx -= tile.x; dy -= tile.y; var tex = tile.getTexture(); var pixWidth = 1 / tex.width; var pixHeight = 1 / tex.height; ma = pixWidth / scaleX; mb = 0; mc = 0; md = pixHeight / scaleY; mx = -dx * ma; my = -dy * md; } /** Draws a Tile at given position. See `Graphics.beginTileFill` for limitations. This methods ends current fill operation. @param x The X position of the tile. @param y The Y position of the tile. @param tile The tile to draw. **/ public function drawTile( x : Float, y : Float, tile : h2d.Tile ) { beginTileFill(x, y, tile); drawRect(x, y, tile.width, tile.height); endFill(); } /** Sets an outline style. Changing the line style ends the currently drawn line. @param size Width of the outline. Setting size to 0 will remove the outline. @param color An outline RGB color. @param alpha An outline transparency. **/ public function lineStyle( size : Float = 0, color = 0, alpha = 1. ) { flush(); this.lineSize = size; lineA = alpha; lineR = ((color >> 16) & 0xFF) / 255.; lineG = ((color >> 8) & 0xFF) / 255.; lineB = (color & 0xFF) / 255.; } /** Ends the current line and starts new one at given position. **/ public inline function moveTo(x,y) { flush(); lineTo(x, y); } /** Ends the current fill operation. **/ public function endFill() { flush(); doFill = false; } /** Changes current fill color. Does not interrupt current fill operation and can be utilized to customize color per vertex. During tile fill operation, color serves as a tile color multiplier. @param color The new fill color. @param alpha The new fill transparency. **/ public inline function setColor( color : Int, alpha : Float = 1. ) { curA = alpha; curR = ((color >> 16) & 0xFF) / 255.; curG = ((color >> 8) & 0xFF) / 255.; curB = (color & 0xFF) / 255.; } /** Draws a rectangle with given parameters. @param x The rectangle top-left corner X position. @param y The rectangle top-left corner Y position. @param w The rectangle width. @param h The rectangle height. **/ public function drawRect( x : Float, y : Float, w : Float, h : Float ) { flush(); lineTo(x, y); lineTo(x + w, y); lineTo(x + w, y + h); lineTo(x, y + h); lineTo(x, y); var e = 0.01; // see #776 tmpPoints[0].x += e; tmpPoints[0].y += e; tmpPoints[1].y += e; tmpPoints[3].x += e; tmpPoints[4].x += e; tmpPoints[4].y += e; flush(); } /** Draws a rounded rectangle with given parameters. @param x The rectangle top-left corner X position. @param y The rectangle top-left corner Y position. @param w The rectangle width. @param h The rectangle height. @param radius Radius of the rectangle corners. @param nsegments Amount of segments used for corners. When `0` segment count calculated automatically. **/ public function drawRoundedRect( x : Float, y : Float, w : Float, h : Float, radius : Float, nsegments = 0 ) { if (radius <= 0) { return drawRect(x, y, w, h); } x += radius; y += radius; w -= radius * 2; h -= radius * 2; flush(); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(radius * hxd.Math.degToRad(90) / 4)); if( nsegments < 3 ) nsegments = 3; var angle = hxd.Math.degToRad(90) / (nsegments - 1); inline function corner(x, y, angleStart) { for ( i in 0...nsegments) { var a = i * angle + hxd.Math.degToRad(angleStart); lineTo(x + Math.cos(a) * radius, y + Math.sin(a) * radius); } } lineTo(x, y - radius); lineTo(x + w, y - radius); corner(x + w, y, 270); lineTo(x + w + radius, y + h); corner(x + w, y + h, 0); lineTo(x, y + h + radius); corner(x, y + h, 90); lineTo(x - radius, y); corner(x, y, 180); flush(); } /** Draws a circle centered at given position. @param cx X center position of the circle. @param cy Y center position of the circle. @param radius Radius of the circle. @param nsegments Amount of segments used to draw the circle. When `0`, amount of segments calculated automatically. **/ public function drawCircle( cx : Float, cy : Float, radius : Float, nsegments = 0 ) { flush(); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(radius * 3.14 * 2 / 4)); if( nsegments < 3 ) nsegments = 3; var angle = Math.PI * 2 / nsegments; for( i in 0...nsegments + 1 ) { var a = i * angle; lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius); } flush(); } /** Draws an ellipse centered at given position. @param cx X center position of the ellipse. @param cy Y center position of the ellipse. @param radiusX Horizontal radius of an ellipse. @param radiusY Vertical radius of an ellipse. @param rotationAngle Ellipse rotation in radians. @param nsegments Amount of segments used to draw an ellipse. When `0`, amount of segments calculated automatically. **/ public function drawEllipse( cx : Float, cy : Float, radiusX : Float, radiusY : Float, rotationAngle : Float = 0, nsegments = 0 ) { flush(); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(radiusY * 3.14 * 2 / 4)); if( nsegments < 3 ) nsegments = 3; var angle = Math.PI * 2 / nsegments; var x1, y1; for( i in 0...nsegments + 1 ) { var a = i * angle; x1 = Math.cos(a) * Math.cos(rotationAngle) * radiusX - Math.sin(a) * Math.sin(rotationAngle) * radiusY; y1 = Math.cos(rotationAngle) * Math.sin(a) * radiusY + Math.cos(a) * Math.sin(rotationAngle) * radiusX; lineTo(cx + x1, cy + y1); } flush(); } /** Draws a pie centered at given position. @param cx X center position of the pie. @param cy Y center position of the pie. @param radius Radius of the pie. @param angleStart Starting angle of the pie in radians. @param angleLength The pie size in clockwise direction with `2*PI` being full circle. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically. **/ public function drawPie( cx : Float, cy : Float, radius : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) { if(Math.abs(angleLength) >= Math.PI * 2) { return drawCircle(cx, cy, radius, nsegments); } flush(); lineTo(cx, cy); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(radius * angleLength / 4)); if( nsegments < 3 ) nsegments = 3; var angle = angleLength / (nsegments - 1); for( i in 0...nsegments ) { var a = i * angle + angleStart; lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius); } lineTo(cx, cy); flush(); } /** Draws a double-edged pie centered at given position. @param cx X center position of the pie. @param cy Y center position of the pie. @param radius The outer radius of the pie. @param innerRadius The inner radius of the pie. @param angleStart Starting angle of the pie in radians. @param angleLength The pie size in clockwise direction with `2*PI` being full circle. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically. **/ public function drawPieInner( cx : Float, cy : Float, radius : Float, innerRadius : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) { flush(); if( Math.abs(angleLength) >= Math.PI * 2 + 1e-3 ) angleLength = Math.PI*2+1e-3; var cs = Math.cos(angleStart); var ss = Math.sin(angleStart); var ce = Math.cos(angleStart + angleLength); var se = Math.sin(angleStart + angleLength); lineTo(cx + cs * innerRadius, cy + ss * innerRadius); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(radius * angleLength / 4)); if( nsegments < 3 ) nsegments = 3; var angle = angleLength / (nsegments - 1); for( i in 0...nsegments ) { var a = i * angle + angleStart; lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius); } lineTo(cx + ce * innerRadius, cy + se * innerRadius); for( i in 0...nsegments ) { var a = (nsegments - 1 - i) * angle + angleStart; lineTo(cx + Math.cos(a) * innerRadius, cy + Math.sin(a) * innerRadius); } flush(); } /** Draws a rectangular pie centered at given position. @param cx X center position of the pie. @param cy Y center position of the pie. @param width Width of the pie. @param height Height of the pie. @param angleStart Starting angle of the pie in radians. @param angleLength The pie size in clockwise direction with `2*PI` being solid rectangle. @param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically. **/ public function drawRectanglePie( cx : Float, cy : Float, width : Float, height : Float, angleStart:Float, angleLength:Float, nsegments = 0 ) { if(Math.abs(angleLength) >= Math.PI*2) { return drawRect(cx-(width/2), cy-(height/2), width, height); } flush(); lineTo(cx, cy); if( nsegments == 0 ) nsegments = Math.ceil(Math.abs(Math.max(width, height) * angleLength / 4)); if( nsegments < 3 ) nsegments = 3; var angle = angleLength / (nsegments - 1); var square2 = Math.sqrt(2); for( i in 0...nsegments ) { var a = i * angle + angleStart; var _width = Math.cos(a) * (width/2+1) * square2; var _height = Math.sin(a) * (height/2+1) * square2; _width = Math.abs(_width) >= width/2 ? (Math.cos(a) < 0 ? width/2*-1 : width/2) : _width; _height = Math.abs(_height) >= height/2 ? (Math.sin(a) < 0 ? height/2*-1 : height/2) : _height; lineTo(cx + _width, cy + _height); } lineTo(cx, cy); flush(); } /** * 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. * IvanK Lib port ( http://lib.ivank.net ) */ public function curveTo( bx : Float, by : Float, cx : Float, cy : Float) { var ax = tmpPoints.length == 0 ? 0 :tmpPoints[ tmpPoints.length - 1 ].x; var ay = tmpPoints.length == 0 ? 0 :tmpPoints[ tmpPoints.length - 1 ].y; var t = 2 / 3; cubicCurveTo(ax + t * (bx - ax), ay + t * (by - ay), cx + t * (bx - cx), cy + t * (by - cy), cx, cy); } /** * Draws a cubic Bezier curve from the current drawing position to the specified anchor point. * IvanK Lib port ( http://lib.ivank.net ) * @param bx control X for start point * @param by control Y for start point * @param cx control X for end point * @param cy control Y for end point * @param dx end X * @param dy end Y * @param nsegments = 40 */ public function cubicCurveTo( bx : Float, by : Float, cx : Float, cy : Float, dx : Float, dy : Float, nsegments = 40) { var ax = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].x; var ay = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].y; var tobx = bx - ax, toby = by - ay; var tocx = cx - bx, tocy = cy - by; var todx = dx - cx, tody = dy - cy; var step = 1 / nsegments; for (i in 1...nsegments) { var d = i * step; var px = ax + d * tobx, py = ay + d * toby; var qx = bx + d * tocx, qy = by + d * tocy; var rx = cx + d * todx, ry = cy + d * tody; var toqx = qx - px, toqy = qy - py; var torx = rx - qx, tory = ry - qy; var sx = px + d * toqx, sy = py + d * toqy; var tx = qx + d * torx, ty = qy + d * tory; var totx = tx - sx, toty = ty - sy; lineTo(sx + d * totx, sy + d * toty); } lineTo(dx, dy); } /** Draws a straight line from the current drawing position to the given position. **/ public inline function lineTo( x : Float, y : Float ) { addVertex(x, y, curR, curG, curB, curA, x * ma + y * mc + mx, x * mb + y * md + my); } /** Advanced usage. Adds new vertex to the current polygon with given parameters and current line style. @param x Vertex X position @param y Vertex Y position @param r Red tint value of the vertex when performing fill operation. @param g Green tint value of the vertex when performing fill operation. @param b Blue tint value of the vertex when performing fill operation. @param a Alpha of the vertex when performing fill operation. @param u Normalized horizontal Texture position from the current Tile fill operation. @param v Normalized vertical Texture position from the current Tile fill operation. **/ public function addVertex( x : Float, y : Float, r : Float, g : Float, b : Float, a : Float, u : Float = 0., v : Float = 0. ) { var half = lineSize / 2.0; if( x - half < xMin ) xMin = x - half; if( y - half < yMin ) yMin = y - half; if( x + half > xMax ) xMax = x + half; if( y + half > yMax ) yMax = y + half; if( x < xMinSize ) xMinSize = x; if( y < yMinSize ) yMinSize = y; if( x > xMaxSize ) xMaxSize = x; if( y > yMaxSize ) yMaxSize = y; if( doFill ) content.add(x, y, u, v, r, g, b, a); var gp = new GPoint(); gp.load(x, y, lineR, lineG, lineB, lineA); tmpPoints.push(gp); } override function draw(ctx:RenderContext) { if( !ctx.beginDrawBatchState(this) ) return; content.doRender(ctx); } override function sync(ctx:RenderContext) { super.sync(ctx); flush(); content.flush(); } }