diff --git a/flixel/addons/display/FlxPieDial.hx b/flixel/addons/display/FlxPieDial.hx index 0c50c9da..e5e8d382 100644 --- a/flixel/addons/display/FlxPieDial.hx +++ b/flixel/addons/display/FlxPieDial.hx @@ -1,18 +1,23 @@ package flixel.addons.display; import flixel.FlxSprite; +import flixel.graphics.FlxGraphic; +import flixel.graphics.tile.FlxGraphicsShader; import flixel.math.FlxMath; import flixel.math.FlxPoint; +import flixel.util.FlxBitmapDataPool; import flixel.util.FlxColor; +import flixel.util.FlxSpriteUtil; import openfl.display.BitmapData; import openfl.display.BitmapDataChannel; import openfl.display.BlendMode; +import openfl.geom.Point; +import openfl.geom.Rectangle; using flixel.util.FlxSpriteUtil; /** * A dynamic shape that fills up the way a pie chart does. Useful for timers and other things. - * @author larsiusprime */ class FlxPieDial extends FlxSprite { @@ -20,269 +25,356 @@ class FlxPieDial extends FlxSprite * A value between 0.0 (empty) and 1.0 (full) */ public var amount(default, set):Float; - - var pieFrames:Int = 0; - - public function new(X:Float, Y:Float, Radius:Int, Color:FlxColor = FlxColor.WHITE, Frames:Int = 36, ?Shape:FlxPieDialShape, Clockwise:Bool = true, - InnerRadius:Int = 0) + + public function new(x = 0.0, y = 0.0, radius:Int, color = FlxColor.WHITE, frames = 36, ?shape:FlxPieDialShape, clockwise = true, innerRadius = 0) { - if (Shape == null) - Shape = CIRCLE; - super(X, Y); - makePieDialGraphic(Radius, Color, Frames, Shape, Clockwise, InnerRadius); + if (shape == null) + shape = CIRCLE; + + super(x, y); + getPieDialGraphic(radius, color, frames, shape, clockwise, innerRadius); amount = 1.0; } override public function draw():Void { - if (amount == 0) + if (amount * animation.numFrames < 1) return; + super.draw(); } - - function makePieDialGraphic(Radius:Int, Color:FlxColor, Frames:Int, Shape:FlxPieDialShape, Clockwise:Bool, InnerRadius:Int) + + function getPieDialGraphic(radius:Int, color:FlxColor, frames:Int, shape:FlxPieDialShape, clockwise:Bool, innerRadius:Int) { - pieFrames = Frames; - var key:String = "pie_dial_" + Color.toHexString() + "_" + Radius + "_" + Frames + "_" + Shape + "_" + Clockwise + "_" + InnerRadius; - var W = Radius * 2; - var H = Radius * 2; - if (!FlxG.bitmap.checkCache(key)) - { - var bmp = makePieDialGraphicSub(Radius, Color, Frames, Shape, Clockwise, InnerRadius); - FlxG.bitmap.add(bmp, true, key); - } - - loadGraphic(key, true, W, H); + final graphic = FlxPieDialUtils.getPieDialGraphic(radius, color, frames, shape, clockwise, innerRadius); + loadGraphic(graphic, true, radius * 2, radius * 2); } - - function makePieDialGraphicSub(Radius:Int, Color:Int, Frames:Int, Shape:FlxPieDialShape, Clockwise:Bool, InnerRadius):BitmapData + + function set_amount(f:Float):Float { - var W = Radius * 2; - var H = Radius * 2; - - var rows:Int = Math.ceil(Math.sqrt(Frames)); - var cols:Int = Math.ceil((Frames) / rows); - - var back = Clockwise ? FlxColor.BLACK : FlxColor.WHITE; - var fore = Clockwise ? FlxColor.WHITE : FlxColor.BLACK; - - var fullFrame = makeFullFrame(Radius, Color, Frames, Shape, Clockwise, InnerRadius); - var nextFrame = new FlxSprite().makeGraphic(W, H, back, false); - - var bmp:BitmapData = new BitmapData(W * cols, H * rows, false, back); - var i:Int = 0; - _flashPoint.setTo(0, 0); - var p:FlxPoint = FlxPoint.get(0, -1); - var degrees:Float = 360 / (Frames); - if (!Clockwise) + amount = FlxMath.bound(f, 0.0, 1.0); + var frame:Int = Std.int(f * animation.numFrames); + animation.frameIndex = frame; + if (amount == 1.0) { - degrees *= -1; + animation.frameIndex = 0; // special case for full frame } + return amount; + } +} - var sweep:Float = Clockwise ? 0 : 360; - var bmp2 = new BitmapData(bmp.width, bmp.height, true, FlxColor.TRANSPARENT); - var fullBmp:BitmapData = fullFrame.pixels.clone(); +enum FlxPieDialShape +{ + CIRCLE; + SQUARE; +} - var polygon:Array = [FlxPoint.get(), FlxPoint.get(), FlxPoint.get(), FlxPoint.get(), FlxPoint.get()]; - for (r in 0...rows) +/** + * Set of tools for drawing pie dial graphics + * @since 5.9.0 + */ +class FlxPieDialUtils +{ + static final _rect = new Rectangle(); + static final _zero = new Point(); + static final _point = new Point(); + static var flashGfx = FlxSpriteUtil.flashGfx; + + public static function getPieDialGraphic(radius:Int, color:FlxColor, frames:Int, shape:FlxPieDialShape, clockwise:Bool, innerRadius:Int) + { + final key = 'pie_dial_${color.toHexString()}_${radius}_${frames}_${shape}_${clockwise}_$innerRadius'; + + if (!FlxG.bitmap.checkCache(key)) { - for (c in 0...cols) - { - if (i >= Frames) - { - break; - } - - _flashPoint.setTo(c * W, r * H); - bmp2.copyPixels(fullBmp, fullBmp.rect, _flashPoint); - - if (i <= 0) - { - bmp.fillRect(fullBmp.rect, FlxColor.WHITE); - } - else - { - nextFrame.pixels.copyPixels(fullFrame.pixels, fullFrame.pixels.rect, _flashPointZero); - _flashPoint.setTo(c * W, r * H); - drawSweep(sweep, p, nextFrame, polygon, W, H, back, fore); - bmp.copyPixels(nextFrame.pixels, nextFrame.pixels.rect, _flashPoint); - } - - sweep += degrees; - p.rotateByDegrees(degrees); - - i++; - } - - if (i >= Frames) - { - break; - } + final bmp = renderPieDial(shape, radius, innerRadius, frames, clockwise, color); + FlxG.bitmap.add(bmp, true, key); } - - fullBmp.dispose(); - fullFrame.destroy(); - nextFrame.destroy(); - - var shapeChannel = new BitmapData(bmp.width, bmp.height, false); - shapeChannel.copyChannel(bmp2, bmp2.rect, _flashPointZero, BitmapDataChannel.ALPHA, BitmapDataChannel.RED); - shapeChannel.copyChannel(bmp2, bmp2.rect, _flashPointZero, BitmapDataChannel.ALPHA, BitmapDataChannel.GREEN); - shapeChannel.copyChannel(bmp2, bmp2.rect, _flashPointZero, BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE); - - shapeChannel.draw(bmp, null, null, BlendMode.MULTIPLY, null, true); - bmp2.copyChannel(shapeChannel, shapeChannel.rect, _flashPointZero, BitmapDataChannel.RED, BitmapDataChannel.ALPHA); - - shapeChannel.dispose(); - bmp.dispose(); - - return bmp2; + + return FlxG.bitmap.get(key); } - - function makeFullFrame(Radius:Int, Color:Int, Frames:Int, Shape:FlxPieDialShape, Clockwise:Bool, InnerRadius):FlxSprite + + public static function getRadialGaugeGraphic(shape:FlxPieDialShape, radius:Int, innerRadius = 0, color = FlxColor.WHITE) { - var W = Radius * 2; - var H = Radius * 2; - - var fullFrame = new FlxSprite().makeGraphic(W, H, FlxColor.TRANSPARENT, true); - if (InnerRadius > Radius) - { - InnerRadius = 0; - } - - var dR = Radius - InnerRadius; - - if (Shape == SQUARE) + final key = 'radial_gauge_${shape}_${color.toHexString()}_${radius}_$innerRadius'; + + if (!FlxG.bitmap.checkCache(key)) { - fullFrame.pixels.fillRect(fullFrame.pixels.rect, Color); - if (InnerRadius > 0) - { - _flashRect.setTo(dR, dR, InnerRadius * 2, InnerRadius * 2); - fullFrame.pixels.fillRect(_flashRect, FlxColor.TRANSPARENT); - } + final bmp = renderRadialGauge(shape, radius, innerRadius, color); + FlxG.bitmap.add(bmp, true, key); } - else if (Shape == CIRCLE) + + return FlxG.bitmap.get(key); + } + + /** + * Draws an animated pie dial graphic where each frame shows a more full amount, + * however the full gauge frame is on frame 0 + * + * @param radius The radius of the shape + * @param color The color of the shape + * @param shape The shape, Either `SQUARE` or `CIRCLE` + * @param innerRadius The radius of the inner hollow portion, where `0` means completely filled + */ + public static function renderRadialGauge(shape:FlxPieDialShape, radius:Int, innerRadius = 0, color = FlxColor.WHITE):BitmapData + { + return renderPieDial(shape, radius, innerRadius, 1, true, color); + } + + /** + * Draws an animated pie dial graphic where each frame shows a more full amount, + * however the full gauge frame is on frame 0 + * + * @param radius The radius of the shape + * @param color The color of the shape + * @param frames + * @param shape The shape, Either `SQUARE` or `CIRCLE` + * @param clockwise The direction the gauge + * @param innerRadius The radius of the inner hollow portion, where `0` means completely filled + */ + public static function renderPieDial(shape:FlxPieDialShape, radius:Int, innerRadius:Int, frames:Int, clockwise = true, color = FlxColor.WHITE):BitmapData + { + final W = radius * 2; + final H = radius * 2; + + final rows = Math.ceil(Math.sqrt(frames)); + final cols = Math.ceil(frames / rows); + + final maskFrame = FlxBitmapDataPool.get(W, H, true, FlxColor.TRANSPARENT, true); + final fullFrame = FlxBitmapDataPool.get(W, H, true, FlxColor.TRANSPARENT, true); + FlxPieDialUtils.drawShape(fullFrame, radius, color, shape, innerRadius); + + final result = new BitmapData(W * cols, H * rows, true, FlxColor.TRANSPARENT); + final p = FlxPoint.get(); + final degreeInterval = (clockwise ? 1 : -1) * 360 / frames; + + final mask = FlxBitmapDataPool.get(result.width, result.height, result.transparent, FlxColor.TRANSPARENT, true); + + final polygon:Array = [FlxPoint.get(), FlxPoint.get(), FlxPoint.get(), FlxPoint.get(), FlxPoint.get()]; + for (i in 0...frames) { - if (InnerRadius > 0) + _point.setTo((i % cols) * W, Std.int(i / cols) * H); + result.copyPixels(fullFrame, fullFrame.rect, _point);//, null, null, true); + if (i <= 0) { - var alpha = new BitmapData(fullFrame.pixels.width, fullFrame.pixels.height, false, FlxColor.BLACK); - fullFrame.pixels.fillRect(_flashRect, FlxColor.BLACK); - fullFrame.drawCircle(-1, -1, Radius, FlxColor.WHITE, null, {smoothing: true}); - fullFrame.drawCircle(-1, -1, InnerRadius, FlxColor.BLACK, null, {smoothing: true}); - - alpha.copyPixels(fullFrame.pixels, fullFrame.pixels.rect, _flashPointZero, null, null, true); - - fullFrame.pixels.fillRect(fullFrame.pixels.rect, Color); - fullFrame.pixels.copyChannel(alpha, alpha.rect, _flashPointZero, BitmapDataChannel.RED, BitmapDataChannel.ALPHA); - - alpha.dispose(); + mask.fillRect(fullFrame.rect, FlxColor.WHITE); } else { - fullFrame.drawCircle(-1, -1, Radius, Color); + final angle = degreeInterval * i; + maskFrame.fillRect(maskFrame.rect, FlxColor.TRANSPARENT); + FlxPieDialUtils.drawSweep(maskFrame, angle); + mask.copyPixels(maskFrame, maskFrame.rect, _point, null, null, true); } } - return fullFrame; + + result.copyPixels(result, result.rect, _zero, mask); + FlxBitmapDataPool.put(mask); + FlxBitmapDataPool.put(maskFrame); + FlxBitmapDataPool.put(fullFrame); + + return result; } - - function drawSweep(sweep:Float, p:FlxPoint, nextFrame:FlxSprite, polygon:Array, W:Int, H:Int, back:FlxColor, fore:FlxColor) + + /** + * Draws the specified shape onto the bitmap + * + * @param dest The bitmap to draw to + * @param radius The radius of the shape + * @param color The color of the shape + * @param shape The shape, Either `SQUARE` or `CIRCLE` + * @param innerRadius The radius of the inner hollow portion, where `0` means completely filled + */ + public static inline function drawShape(dest:BitmapData, radius:Int, color:FlxColor, shape:FlxPieDialShape, innerRadius = 0):BitmapData { - var halfW = W / 2; - var halfH = H / 2; - - nextFrame.pixels.fillRect(nextFrame.pixels.rect, back); - polygon[0].set(halfW, halfH); - - if (sweep < 45) + final W = radius << 1; + final H = radius << 1; + + switch (shape) { - polygon[1].set(halfW, 0); - polygon[2].set(halfW + W * p.x, halfH + H * p.y); - polygon[3].set(halfW, halfH); + case SQUARE if (innerRadius > 0 && innerRadius < radius): + final thickness = radius - innerRadius; + _rect.setTo(0, 0, W, thickness); + dest.fillRect(_rect, color); + _rect.setTo(0, 0, thickness, H); + dest.fillRect(_rect, color); + _rect.setTo(W - thickness, 0, thickness, H); + dest.fillRect(_rect, color); + _rect.setTo(0, H - thickness, W, thickness); + dest.fillRect(_rect, color); + + case SQUARE: + dest.fillRect(dest.rect, color); + + case CIRCLE if (innerRadius > 0 && innerRadius < radius): + final alpha = FlxBitmapDataPool.get(W, H, false, FlxColor.BLACK, true); + alpha.fillRect(alpha.rect, FlxColor.BLACK); + drawCircle(alpha, radius, FlxColor.WHITE, null, {smoothing: true}); + drawCircle(alpha, innerRadius, FlxColor.BLACK, null, {smoothing: true}); + + alpha.copyPixels(dest, dest.rect, _zero, null, null, true); + + dest.fillRect(dest.rect, color); + dest.copyChannel(alpha, alpha.rect, _zero, BitmapDataChannel.RED, BitmapDataChannel.ALPHA); + + FlxBitmapDataPool.put(alpha); + + case CIRCLE: + drawCircle(dest, radius, color); } - else if (sweep < 90) + return dest; + } + + /** + * Used via `drawSweep` + */ + static final sweepPoints = [for (i in 0...4) FlxPoint.get()]; + + /** + * Draws a wedge section of a bitmap, used in `FlxPieDial` + * @param dest The btimap to draw to + * @param degrees The angle of the wedge + * @param color The color to fill the wedge + */ + public static function drawSweep(dest:BitmapData, degrees:Float, color = FlxColor.WHITE) + { + degrees %= 360; + final p = sweepPoints; + final radius = dest.width >> 1; + final center = p[0].set(radius, radius); + final cornerLength = center.length; + + if (degrees >= 270) { - polygon[1].set(halfW, 0); - polygon[2].set(W, 0); - polygon[3].set(halfW + W * p.x, halfH + H * p.y); + // fill right half + _rect.setTo(radius, 0, radius, dest.height); + dest.fillRect(_rect, color); + // fill bottom-left quadrant + _rect.setTo(0, radius, radius, radius); + dest.fillRect(_rect, color); } - else if (sweep < 135) + else if (degrees >= 180) { - _flashRect.setTo(halfW, 0, halfW, halfH); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(W, halfH); - polygon[2].set(halfW + W * p.x, halfH + H * p.y); - polygon[3].set(halfW, halfH); + // fill right half + _rect.setTo(radius, 0, radius, dest.height); + dest.fillRect(_rect, color); } - else if (sweep < 180) + else if (degrees >= 90) { - _flashRect.setTo(halfW, 0, halfW, halfH); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(W, halfH); - polygon[2].set(W, H); - polygon[3].set(halfW + W * p.x, halfH + H * p.y); + // fill top-right quadrant + _rect.setTo(radius, 0, radius, radius); + dest.fillRect(_rect, color); } - else if (sweep < 225) + else if (degrees <= -270) { - _flashRect.setTo(halfW, 0, halfW, H); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(halfW, H); - polygon[2].set(halfW + W * p.x, halfH + H * p.y); - polygon[3].set(halfW, halfH); + // fill left half + _rect.setTo(0, 0, radius, dest.height); + dest.fillRect(_rect, color); + // fill bottom-right quadrant + _rect.setTo(radius, radius, radius, radius); + dest.fillRect(_rect, color); } - else if (sweep < 270) + else if (degrees <= -180) { - _flashRect.setTo(halfW, 0, halfW, H); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(halfW, H); - polygon[2].set(0, H); - polygon[3].set(halfW + W * p.x, halfH + H * p.y); + // fill left half + _rect.setTo(0, 0, radius, dest.height); + dest.fillRect(_rect, color); } - else if (sweep < 315) + else if (degrees <= -90) { - _flashRect.setTo(halfW, 0, halfW, H); - nextFrame.pixels.fillRect(_flashRect, fore); - _flashRect.setTo(0, halfH, halfW, halfH); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(0, halfH); - polygon[2].set(halfW + W * p.x, halfH + H * p.y); - polygon[3].set(halfW, halfH); + // fill top-left quadrant + _rect.setTo(0, 0, radius, radius); + dest.fillRect(_rect, color); } - else if (sweep < 360) + + // draw the interesting quadrant + if (Math.abs(degrees % 90) < 45) { - _flashRect.setTo(halfW, 0, halfW, H); - nextFrame.pixels.fillRect(_flashRect, fore); - _flashRect.setTo(0, halfH, halfW, halfH); - nextFrame.pixels.fillRect(_flashRect, fore); - - polygon[1].set(0, halfH); - polygon[2].set(0, 0); - polygon[3].set(halfW + W * p.x, halfH + H * p.y); + p[1].setPolarDegrees(radius, -90 + Std.int(degrees / 90) * 90).addPoint(center); + p[2].setPolarDegrees(cornerLength, -90 + degrees).addPoint(center); + p[3].copyFrom(center); } - - polygon[4].set(halfW, halfH); - - nextFrame.drawPolygon(polygon, fore); + else + { + final quadDegreesStart = Std.int(degrees / 90) * 90; + final cornerDegrees = quadDegreesStart + (degrees < 0 ? -45 : 45); + p[1].setPolarDegrees(radius, -90 + quadDegreesStart).addPoint(center); + p[2].setPolarDegrees(cornerLength, -90 + cornerDegrees).addPoint(center); + p[3].setPolarDegrees(cornerLength, -90 + degrees).addPoint(center); + } + + drawPolygon(dest, p, color); } - - function set_amount(f:Float):Float + + /** + * This function draws a circle on a FlxSprite at position X,Y with the specified color. + * + * @param bitmap The BitmapData to manipulate + * @param X X coordinate of the circle's center (automatically centered on the sprite if -1) + * @param Y Y coordinate of the circle's center (automatically centered on the sprite if -1) + * @param radius Radius of the circle (makes sure the circle fully fits on the sprite's graphic if < 1, assuming and and y are centered) + * @param color The ARGB color to fill this circle with. FlxColor.TRANSPARENT (0x0) means no fill. + * @param lineStyle A LineStyle typedef containing the params of Graphics.lineStyle() + * @param drawStyle A DrawStyle typedef containing the params of BitmapData.draw() + * @return The FlxSprite for chaining + */ + public static function drawCircle(bitmap:BitmapData, ?radius:Float, color = FlxColor.WHITE, ?lineStyle:LineStyle, ?drawStyle:DrawStyle):BitmapData { - amount = FlxMath.bound(f, 0.0, 1.0); - var frame:Int = Std.int(f * pieFrames); - animation.frameIndex = frame; - if (amount == 1.0) + final x = bitmap.width * 0.5; + final y = bitmap.height * 0.5; + + if (radius == null) + radius = Math.min(bitmap.width, bitmap.height) * 0.5; + + beginDraw(color, lineStyle); + flashGfx.drawCircle(x, y, radius); + endDraw(bitmap, drawStyle); + return bitmap; + } + + /** + * This function draws a polygon on a FlxSprite. + * + * @param graphic The FlxSprite to manipulate + * @param Vertices Array of Vertices to use for drawing the polygon + * @param FillColor The ARGB color to fill this polygon with. FlxColor.TRANSPARENT (0x0) means no fill. + * @param lineStyle A LineStyle typedef containing the params of Graphics.lineStyle() + * @param drawStyle A DrawStyle typedef containing the params of BitmapData.draw() + * @return The FlxSprite for chaining + */ + public static function drawPolygon(bitmap:BitmapData, vertices:Array, fillColor = FlxColor.WHITE, ?lineStyle:LineStyle, + ?drawStyle:DrawStyle):BitmapData + { + beginDraw(fillColor, lineStyle); + final p:FlxPoint = vertices.shift(); + flashGfx.moveTo(p.x, p.y); + for (p in vertices) { - animation.frameIndex = 0; // special case for full frame + flashGfx.lineTo(p.x, p.y); } - return amount; + endDraw(bitmap, drawStyle); + vertices.unshift(p); + return bitmap; } -} - -enum FlxPieDialShape -{ - CIRCLE; - SQUARE; -} + + static inline function beginDraw(color:FlxColor, ?lineStyle:LineStyle):Void + { + flashGfx.clear(); + FlxSpriteUtil.setLineStyle(lineStyle); + + if (color != FlxColor.TRANSPARENT) + flashGfx.beginFill(color.rgb, color.alphaFloat); + } + + static inline function endDraw(bitmap:BitmapData, ?style:DrawStyle):BitmapData + { + flashGfx.endFill(); + if (style == null) + style = {smoothing: false}; + else if (style.smoothing == null) + style.smoothing = false; + + final sprite = FlxSpriteUtil.flashGfxSprite; + bitmap.draw(sprite, style.matrix, style.colorTransform, style.blendMode, style.clipRect, style.smoothing); + return bitmap; + } +} \ No newline at end of file diff --git a/flixel/addons/display/FlxRadialGauge.hx b/flixel/addons/display/FlxRadialGauge.hx new file mode 100644 index 00000000..67ba25e4 --- /dev/null +++ b/flixel/addons/display/FlxRadialGauge.hx @@ -0,0 +1,155 @@ +package flixel.addons.display; + +import flixel.FlxSprite; +import flixel.addons.display.FlxPieDial; +import flixel.util.FlxColor; + +#if !flash +/** + * A dynamic shape that fills up radially (like a pie chart). Useful for timers and other things. + * `FlxRadialGauge` uses `FlxRadialWipeShader` to fill the gauge portion, where `FlxPieDial` + * creates an animation. This also works with any graphic, unlike `FlxPieDial` + * @since 5.9.0 + */ +class FlxRadialGauge extends FlxSprite +{ + /** A value between 0.0 (empty) and 1.0 (full) */ + public var amount(get, set):Float; + inline function get_amount():Float + { + return _sweepShader.amount; + } + inline function set_amount(value:Float):Float + { + return _sweepShader.amount = value; + } + + /** The angle in degrees to start the dial fill */ + public var start(get, set):Float; + inline function get_start():Float + { + return _sweepShader.start; + } + inline function set_start(value:Float):Float + { + return _sweepShader.start = value; + } + + /** The angle in degrees to end the dial fill */ + public var end(get, set):Float; + inline function get_end():Float + { + return _sweepShader.end; + } + inline function set_end(value:Float):Float + { + return _sweepShader.end = value; + } + + var _sweepShader(get, never):FlxRadialWipeShader; + inline function get__sweepShader() return cast shader; + + public function new(x = 0.0, y = 0.0, ?simpleGraphic) + { + super(x, y, simpleGraphic); + + shader = new FlxRadialWipeShader(); + this.amount = 1; + } + + public function makeShapeGraphic(shape:FlxRadialGaugeShape, radius:Int, innerRadius = 0, color = FlxColor.WHITE) + { + final graphic = FlxPieDialUtils.getRadialGaugeGraphic(shape, radius, innerRadius, color); + loadGraphic(graphic, true, radius * 2, radius * 2); + } + + public function setOrientation(start = -90.0, end = 270.0) + { + this.start = start; + this.end = end; + } +} + +typedef FlxRadialGaugeShape = FlxPieDialShape; + +/** + * A shader that masks a static sprite radially, based on the `start` and `end` angles + */ +class FlxRadialWipeShader extends flixel.system.FlxAssets.FlxShader +{ + /** The current fill amount, where `0.0` is empty and `1.0` is full */ + public var amount(get, set):Float; + inline function get_amount():Float return _amount.value[0]; + inline function set_amount(value:Float):Float + { + _amount.value = [value]; + return value; + } + + /** The angle in degrees to start the dial fill */ + public var start(get, set):Float; + inline function get_start():Float return _start.value[0]; + inline function set_start(value:Float):Float + { + _start.value = [value]; + return value; + } + + /** The angle in degrees to end the dial fill */ + public var end(get, set):Float; + inline function get_end():Float return _end.value[0]; + inline function set_end(value:Float):Float + { + _end.value = [value]; + return value; + } + + @:glFragmentSource(' + #pragma header + + const float TAU = 6.2831853072; + + uniform float _amount; + uniform float _start; + uniform float _end; + + float getGradiant(in vec2 dist) + { + float start = _start / 360.0; + float delta = (_end - _start) / 360.0; + float angle = atan(dist.y, dist.x) / TAU; + if (_end > _start) + return mod(angle - start, 1.0) / delta; + else + return mod(start - angle, 1.0) / -delta; + } + + float wedge(in vec2 uv, in float ratio) + { + vec2 dist = uv - vec2(0.5); + float grad = getGradiant(dist); + return step(ratio, grad < 0.0 ? 1.0 : grad); + } + + void main() + { + if (_amount > 0.0) + { + float amount = min(1.0, max(0.0, _amount)); + vec4 bitmap = flixel_texture2D(bitmap, openfl_TextureCoordv); + gl_FragColor = mix(bitmap, vec4(0.0), wedge(openfl_TextureCoordv, amount)); + } + else + gl_FragColor = vec4(0.0); + }') + public function new() + { + super(); + amount = 1.0; + start = -90; + end = 270; + } +} +#elseif FLX_NO_COVERAGE_TEST +#error "FlxRadialGauge is not supported on flash targets" +#end \ No newline at end of file