From db1a7924a49d25f7c4e8ddea737a2b37dd6155a9 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Mon, 17 Jun 2024 12:03:32 -0500 Subject: [PATCH 01/15] fix empty result with passed in result arg --- flixel/math/FlxRect.hx | 32 +++++++++-------------- tests/unit/src/flixel/math/FlxRectTest.hx | 30 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/flixel/math/FlxRect.hx b/flixel/math/FlxRect.hx index 31387c58ed..f98397fa2d 100644 --- a/flixel/math/FlxRect.hx +++ b/flixel/math/FlxRect.hx @@ -438,33 +438,27 @@ class FlxRect implements IFlxPooled * Returns the area of intersection with specified rectangle. * If the rectangles do not intersect, this method returns an empty rectangle. * - * @param rect Rectangle to check intersection against. - * @return The area of intersection of two rectangles. + * @param rect Rectangle to check intersection against + * @param result The resulting instance, if `null`, a new one is created + * @return The area of intersection of two rectangles */ public function intersection(rect:FlxRect, ?result:FlxRect):FlxRect { if (result == null) result = FlxRect.get(); - - var x0:Float = x < rect.x ? rect.x : x; - var x1:Float = right > rect.right ? rect.right : right; - if (x1 <= x0) - { - rect.putWeak(); - return result; - } - - var y0:Float = y < rect.y ? rect.y : y; - var y1:Float = bottom > rect.bottom ? rect.bottom : bottom; - if (y1 <= y0) - { - rect.putWeak(); - return result; - } - + + final x0:Float = x < rect.x ? rect.x : x; + final x1:Float = right > rect.right ? rect.right : right; + final y0:Float = y < rect.y ? rect.y : y; + final y1:Float = bottom > rect.bottom ? rect.bottom : bottom; rect.putWeak(); + + if (x1 <= x0 || y1 <= y0) + return result.set(0, 0, 0, 0); + return result.set(x0, y0, x1 - x0, y1 - y0); } + } /** * The middle point of this rect diff --git a/tests/unit/src/flixel/math/FlxRectTest.hx b/tests/unit/src/flixel/math/FlxRectTest.hx index b2faa081ad..74ad399de2 100644 --- a/tests/unit/src/flixel/math/FlxRectTest.hx +++ b/tests/unit/src/flixel/math/FlxRectTest.hx @@ -94,4 +94,34 @@ class FlxRectTest extends FlxTest pivot.put(); expected.put(); } + + @Test + function testIntersection() + { + rect1.set(0, 0, 100, 100); + rect2.set(50, 50, 100, 100); + + final expected = FlxRect.get(50, 50, 50, 50); + final result = FlxRect.get(); + rect1.intersection(rect2, result); + FlxAssert.rectsNear(expected, result, 0.0001); + + expected.put(); + result.put(); + } + + @Test + function testIntersectionEmpty() + { + rect1.set(0, 0, 100, 100); + rect2.set(200, 200, 100, 100); + + final expected = FlxRect.get(0, 0, 0, 0); + final result = FlxRect.get(1000, 1000, 1000, 1000); + rect1.intersection(rect2, result); + FlxAssert.rectsNear(expected, result, 0.0001); + + expected.put(); + result.put(); + } } From 83e93166d89b080037d4216a42c5b3c65053b020 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Mon, 17 Jun 2024 12:05:17 -0500 Subject: [PATCH 02/15] add rect.clipTo --- flixel/math/FlxRect.hx | 14 +++++++++++- tests/unit/src/flixel/math/FlxRectTest.hx | 26 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/flixel/math/FlxRect.hx b/flixel/math/FlxRect.hx index f98397fa2d..a21c297040 100644 --- a/flixel/math/FlxRect.hx +++ b/flixel/math/FlxRect.hx @@ -433,7 +433,7 @@ class FlxRect implements IFlxPooled rect.putWeak(); return result; } - + /** * Returns the area of intersection with specified rectangle. * If the rectangles do not intersect, this method returns an empty rectangle. @@ -458,6 +458,18 @@ class FlxRect implements IFlxPooled return result.set(x0, y0, x1 - x0, y1 - y0); } + + /** + * Resizes `this` instance so that it fits within the intersection of the this and + * the target rect. If there is no overlap between them, The result is an empty rect. + * + * @param rect Rectangle to check intersection against + * @return This rect, useful for chaining + * @since 5.9.0 + */ + public function clipTo(rect:FlxRect):FlxRect + { + return rect.intersection(this, this); } /** diff --git a/tests/unit/src/flixel/math/FlxRectTest.hx b/tests/unit/src/flixel/math/FlxRectTest.hx index 74ad399de2..2f501a65de 100644 --- a/tests/unit/src/flixel/math/FlxRectTest.hx +++ b/tests/unit/src/flixel/math/FlxRectTest.hx @@ -124,4 +124,30 @@ class FlxRectTest extends FlxTest expected.put(); result.put(); } + + @Test + function testClipTo() + { + rect1.set(0, 0, 100, 100); + rect2.set(50, 50, 100, 100); + + final expected = FlxRect.get(50, 50, 50, 50); + rect1.clipTo(rect2); + FlxAssert.rectsNear(expected, rect1, 0.0001); + + expected.put(); + } + + @Test + function testClipToEmpty() + { + rect1.set(0, 0, 100, 100); + rect2.set(200, 200, 100, 100); + + final expected = FlxRect.get(0, 0, 0, 0); + rect1.clipTo(rect2); + FlxAssert.rectsNear(expected, rect1, 0.0001); + + expected.put(); + } } From 8c35c0306823d30e4fb0e3c202140f0d345ea3b5 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 13:30:49 -0500 Subject: [PATCH 03/15] add FlxBuffer --- flixel/system/FlxBuffer.hx | 392 ++++++++++++++++++ tests/unit/src/flixel/system/FlxBufferTest.hx | 58 +++ 2 files changed, 450 insertions(+) create mode 100644 flixel/system/FlxBuffer.hx create mode 100644 tests/unit/src/flixel/system/FlxBufferTest.hx diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx new file mode 100644 index 0000000000..f727a8311a --- /dev/null +++ b/flixel/system/FlxBuffer.hx @@ -0,0 +1,392 @@ +package flixel.system; + +import haxe.macro.TypeTools; +#if macro +import haxe.macro.Type; +import haxe.macro.Expr; +import haxe.macro.Context; + +using haxe.macro.Tools; + +class BufferMacro +{ + public static function build(includeGetters = true):ComplexType + { + final local = Context.getLocalType(); + switch local { + // Extract the type parameter + case TInst(local, [type]): + return buildBuffer(getFields(type, includeGetters), type); + default: + throw "Expected TInst"; + } + } + + static function getFields(type:Type, includeGetters:Bool):Array + { + // Follow it to get the underlying anonymous structure + switch type.follow() + { + case TAbstract(abType, []): + final fields = getFields(abType.get().type, includeGetters).copy(); + // add abstract fields too + if (includeGetters && (abType.get().impl != null || abType.get().impl.get() != null)) + { + final statics = abType.get().impl.get().statics.get(); + for (field in statics) + { + if (field.kind.match(FVar(AccCall, _))) + { + fields.push(field); + } + } + } + return fields; + case TAnonymous(type): + return type.get().fields; + case TInst(found, _): + throw 'Expected type parameter to be an anonymous structure, got ${found.get().name}'; + case TEnum(found, _): + throw 'Expected type parameter to be an anonymous structure, got ${found.get().name}'; + case found: + throw 'Expected type parameter to be an anonymous structure, got ${found.getName()}'; + } + } + + static function buildBuffer(fields:Array, type:Type) + { + // Distinguish getters from actual fields + final getters:Array = fields.copy(); + var i = fields.length; + while (i-- > 0) + { + // todo: Prevent double adds for getters over + final field = fields[i]; + if (field.kind.match(FVar(AccCall, _))) + { + fields.remove(field); + } + } + + final arrayType = fields[0].type; + final arrayTypeName = getTypeName(arrayType); + final prefix = arrayTypeName + "_" + getTypeIdentifier(type); + final name = "Buffer_" + prefix; + final iterName = "BufferIterator_" + prefix; + final kvIterName = "BufferKeyValueIterator_" + prefix; + final complexType = type.toComplexType(); + + // First check whether the generated type already exists + try + { + Context.getType(name); + Context.getType(iterName); + Context.getType(kvIterName); + + // Return a `ComplexType` for the generated type + return TPath({pack: [], name: name}); + } + catch (e) {} // The generated type doesn't exist yet + + final length = fields.length; + if (length < 2) + throw "Just use an array"; + + for (i in 1...length) + { + if (arrayType.toString() != fields[i].type.toString()) + { + throw 'Cannot build buffer for type: { ${fields.map((f)->f.type.toString()).join(", ")} }'; + } + } + + final objectDecl:Expr = + { + pos: Context.currentPos(), + expr: EObjectDecl([ + for (i => field in fields) + { + { + field: field.name, + expr: macro this[iReal + $v{i}], + } + } + ]) + } + + final pushEachBody = + [ + for (field in fields) + { + macro this.push($i{field.name}); + } + ]; + pushEachBody.push(macro return length); + final pushEachFunc:Field = + { + doc:"Creates an item with the given values and adds it at the end of this Buffer and returns the new length of this Array.\n\n" + + "This operation modifies this Array in place.\n\n" + + "this.length increases by 1.", + pos: Context.currentPos(), + name: "push", + access: [APublic, AOverload, AExtern, AInline], + kind:FFun + ({ + args: + [ + for (field in fields) + { + { + type: field.type.toComplexType(), + name: field.name + } + } + ], + expr:macro $b{pushEachBody} + }) + } + + final pushItemBody = + [ + for (field in fields) + { + final name = field.name; + macro this.push(item.$name); + } + ]; + pushItemBody.push(macro return length); + + final pushItemFunc:Field = + { + doc:"Adds the item at the end of this Buffer and returns the new length of this Array.\n\n" + + "This operation modifies this Array in place.\n\n" + + "this.length increases by 1.", + pos: Context.currentPos(), + name: "push", + access: [APublic, AOverload, AExtern, AInline], + kind:FFun + ({ + args: [{name: "item", type: complexType}], + expr:macro $b{pushItemBody} + }) + } + + final iterCt = { name: iterName, pack: [] }; + final kvIterCt = { name: kvIterName, pack: [] }; + + final def = macro class $name + { + static inline var FIELDS:Int = $v{length}; + + /** The number of items in this buffer */ + public var length(get, never):Int; + inline function get_length() return Std.int(this.length / FIELDS); + + public inline function new () + { + this = new openfl.Vector(); + } + + /** Fetches the item at the desired index */ + @:arrayAccess + public inline function get(i:Int):$complexType + { + final iReal = i * FIELDS; + return $e{objectDecl}; + } + + /** Removes and returns the first item */ + public inline function shift():$complexType + { + final iReal = 0; + final item = $e{objectDecl}; + $b{[ + for (field in fields) + { + macro this.shift(); + } + ]} + return item; + } + + /** Removes and returns the last item */ + public inline function pop():$complexType + { + final iReal = this.length - FIELDS; + final item = $e{objectDecl}; + $b{[ + for (field in fields) + { + macro this.pop(); + } + ]} + return item; + } + + /** + * Set the length of the Array. + * If len is shorter than the array's current size, the last length - len elements will + * be removed. If len is longer, the Array will be extended + * + * **Note:** Since FlxBuffers are actually Arrays of some primitive, often `Float`, it + * will likely add all zeros + * + * @param length The desired length + */ + public inline function resize(length:Int) + { + this.length = length * FIELDS; + } + + /** Returns an iterator of the buffer's items */ + public inline function iterator() + { + return new $iterCt(this); + } + + /** Returns an iterator of the buffer's indices and items */ + public inline function keyValueIterator() + { + return new $kvIterCt(this); + } + }; + + def.fields.push(pushEachFunc); + def.fields.push(pushItemFunc); + for (i => field in getters) + { + final getter:Field = + { + pos: Context.currentPos(), + name: "get" + field.name.charAt(0).toUpperCase() + field.name.substr(1), + access: [APublic, AInline], + kind:FFun + ({ + args: [{name: "index", type: (macro:Int)}], + expr:macro this[index * FIELDS + $v{i}] + }) + }; + def.fields.push(getter); + } + + final itemTypeName = getTypeName(type); + def.doc = 'An `openfl.Vector<$arrayTypeName>` disguised as an `Array<$itemTypeName>`.' + + "\nOften used in under-the-hood Flixel systems, like rendering," + + "\nwhere creating actual instances of objects every frame would balloon memory." + + "\n" + + "\n## Example" + + "\nIn the following example, see how it behaves quite similar to an Array" + + "\n```haxe" + + "\n var buffer = new FlxBuffer<{final x:Float; final y:Float;}>();" + + "\n for (i in 0...100)" + + "\n buffer.push({ x: i % 10, y: Std.int(i / 10) });" + + "\n " + + "\n buffer.shift();" + + "\n buffer.pop();" + + "\n " + + "\n for (i=>item in buffer)" + + "\n {" + + "\n trace('$i: $item');" + + "\n }" + + "\n```" + + "\n" + + "\n## Caveats" + + "\n- Can only be modified via `push`, `pop`, `shift` and `resize`. Missing notable" + + "\nfeatures like `insert` and setting via array access operator, these can be" + + "\nimplemented but are low priority" + + "\n- Editing items retrieved from the buffer will not edit the corresponding indicies," + + "\nfor that reason it is recommended to use final vars"; + // `macro class` gives a TDClass, so that needs to be replaced + final arrayCT = arrayType.toComplexType(); + def.kind = TDAbstract((macro:openfl.Vector<$arrayCT>), [macro:openfl.Vector<$arrayCT>], [macro:openfl.Vector<$arrayCT>]); + Context.defineType(def); + + final bufferCt = Context.getType(name).toComplexType(); + final iterDef = macro class $iterName + { + var list:$bufferCt; + var length:Int; + var i:Int; + + inline public function new(list:$bufferCt) + { + this.list = list; + this.length = list.length; + i = 0; + } + + inline public function hasNext() + { + return i < length; + } + + inline public function next() + { + return list[i++]; + } + } + + // `macro class` gives a TDClass, so that needs to be replaced + // iterDef.kind = TDClass(null, [], false, false, false); + Context.defineType(iterDef); + + final kvIterDef = macro class $kvIterName + { + var list:$bufferCt; + var length:Int; + var i:Int; + + inline public function new(list:$bufferCt) + { + this.list = list; + this.length = list.length; + i = 0; + } + + inline public function hasNext() + { + return i < length; + } + + inline public function next() + { + final key = i++; + return { key:key, value:list.get(key) }; + } + } + + // `macro class` gives a TDClass, so that needs to be replaced + // iterDef.kind = TDClass(null, [], false, false, false); + Context.defineType(kvIterDef); + + // Return a `ComplexType` for the generated type + return TPath({pack: [], name: name}); + } + + static function getTypeIdentifier(type:Type) + { + return switch(type) + { + case TAnonymous(type): type.get().fields.map((f)->'${f.name}').join("_"); + default: getTypeName(type); + } + } + + static function getTypeName(type:Type) + { + return switch(type) + { + case TAbstract(type, []): type.get().name; + case TAnonymous(type): + '{ ${type.get().fields.map((f)->'${f.name}: ${getTypeName(f.type)}').join(", ")} }'; + case TInst(type, []): type.get().name; + case TType(type, []): type.get().name; + default: type.getName(); + } + } +} +#else + +@:genericBuild(flixel.system.FlxBuffer.BufferMacro.build()) +class FlxBuffer {} +#end \ No newline at end of file diff --git a/tests/unit/src/flixel/system/FlxBufferTest.hx b/tests/unit/src/flixel/system/FlxBufferTest.hx new file mode 100644 index 0000000000..2129ceb667 --- /dev/null +++ b/tests/unit/src/flixel/system/FlxBufferTest.hx @@ -0,0 +1,58 @@ +package flixel.system; + +import massive.munit.Assert; + +class FlxBufferTest +{ + @Test + function testAll() + { + final bufferStr = new FlxBuffer<{ x:String, y:String }>(); + for (i in 0...100) + bufferStr.push({ x: Std.string(i % 10), y: Std.string(Std.int(i / 10)) }); + + bufferStr.shift(); + bufferStr.pop(); + + // make sure it compiles + for (i=>item in bufferStr) + { + item; + i; + } + + final bufferTD = new FlxBuffer<{ x:Float, y:Float }>(); + final bufferTD2 = new FlxBuffer(); + Assert.areEqual(1, bufferTD.push(5, 10)); + Assert.areEqual(5, bufferTD.getX(0)); + Assert.areEqual(10, bufferTD.getY(0)); + + final buffer = new FlxBuffer(); + for (i in 0...100) + buffer.push({ x: i % 10, y: Std.int(i / 10) }); + + Assert.areEqual(5, buffer.getX(15)); + Assert.areEqual(1, buffer.getY(15)); + Assert.areEqual(6, buffer.getSum(15)); + + // make sure it compiles + for (i=>item in buffer) + { + item; + i; + } + + Assert.areEqual(0, buffer.shift().sum); + Assert.areEqual(18, buffer.pop().sum); + } +} + +typedef XY = { x:Float, y:Float } +@:forward +abstract XYAbs(XY) from XY +{ + public var sum(get, never):Float; + inline function get_sum() { return this.x + this.y; } + + public function toString() { return '( ${this.x} | ${this.y} )'; } +} \ No newline at end of file From b24108429be5a5d0bba74b847098844eddedc7af Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 13:36:32 -0500 Subject: [PATCH 04/15] use FlxBuffer in FlxBitmapText --- flixel/text/FlxBitmapText.hx | 319 +++++++++++---------------- flixel/util/FlxColorTransformUtil.hx | 20 +- 2 files changed, 150 insertions(+), 189 deletions(-) diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index b4b8aa86f1..c69901f9f3 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -4,15 +4,17 @@ import openfl.display.BitmapData; import flixel.FlxBasic; import flixel.FlxG; import flixel.FlxSprite; +import openfl.geom.ColorTransform; import flixel.graphics.frames.FlxBitmapFont; import flixel.graphics.frames.FlxFrame; +import flixel.graphics.tile.FlxDrawQuadsItem; import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.text.FlxText.FlxTextAlign; import flixel.text.FlxText.FlxTextBorderStyle; import flixel.util.FlxColor; import flixel.util.FlxDestroyUtil; -import openfl.geom.ColorTransform; +import flixel.system.FlxBuffer; using flixel.util.FlxColorTransformUtil; @@ -191,9 +193,10 @@ class FlxBitmapText extends FlxSprite var pendingTextBitmapChange:Bool = true; var pendingPixelsChange:Bool = true; - var textData:Array; - var textDrawData:Array; - var borderDrawData:Array; + // var textData:Array; + var textData:FlxBuffer; + var textDrawData:FlxBuffer; + var borderDrawData:FlxBuffer; /** * Helper bitmap buffer for text pixels but without any color transformations @@ -227,10 +230,9 @@ class FlxBitmapText extends FlxSprite } else { - textData = []; - - textDrawData = []; - borderDrawData = []; + textData = new FlxBuffer(); + textDrawData = new FlxBuffer(); + borderDrawData = new FlxBuffer(); } this.text = text; @@ -318,79 +320,34 @@ class FlxBitmapText extends FlxSprite else { checkPendingChanges(true); - - var textLength:Int = Std.int(textDrawData.length / 3); - var borderLength:Int = Std.int(borderDrawData.length / 3); - - var dataPos:Int; - - var cr:Float = color.redFloat; - var cg:Float = color.greenFloat; - var cb:Float = color.blueFloat; - - var borderRed:Float = borderColor.redFloat * cr; - var borderGreen:Float = borderColor.greenFloat * cg; - var borderBlue:Float = borderColor.blueFloat * cb; - var bAlpha:Float = borderColor.alphaFloat * alpha; - - var textRed:Float = cr; - var textGreen:Float = cg; - var textBlue:Float = cb; - var tAlpha:Float = alpha; - - if (useTextColor) - { - textRed *= textColor.redFloat; - textGreen *= textColor.greenFloat; - textBlue *= textColor.blueFloat; - tAlpha *= textColor.alphaFloat; - } - - var bgRed:Float = cr; - var bgGreen:Float = cg; - var bgBlue:Float = cb; - var bgAlpha:Float = alpha; - - if (background) - { - bgRed *= backgroundColor.redFloat; - bgGreen *= backgroundColor.greenFloat; - bgBlue *= backgroundColor.blueFloat; - bgAlpha *= backgroundColor.alphaFloat; - } - - var drawItem; - var currFrame:FlxFrame = null; - var currTileX:Float = 0; - var currTileY:Float = 0; - var sx:Float = scale.x * _facingHorizontalMult; - var sy:Float = scale.y * _facingVerticalMult; - - var ox:Float = origin.x; - var oy:Float = origin.y; - - if (_facingHorizontalMult != 1) - { - ox = frameWidth - ox; - } - if (_facingVerticalMult != 1) + + final FULL = 0xFF*0xFF; + function multiplyColors(a:FlxColor, b:FlxColor) { - oy = frameHeight - oy; + final aR = StringTools.hex(a.red); + final bR = StringTools.hex(b.red); + final aG = StringTools.hex(a.green); + final bG = StringTools.hex(b.green); + final aB = StringTools.hex(a.blue); + final bB = StringTools.hex(b.blue); + final aA = StringTools.hex(a.alpha); + final bA = StringTools.hex(b.alpha); + return FlxColor.fromRGBFloat(a.red * b.red / FULL, a.green * b.green / FULL, a.blue * b.blue / FULL, a.alpha * b.alpha / FULL); } - - var clippedFrameRect; - + + final colorFullAlpha = color.rgb | 0xFF000000; + final borderColorMult = multiplyColors(borderColor, colorFullAlpha); + final bgColorMult = multiplyColors(backgroundColor, colorFullAlpha); + final textColorMult = useTextColor ? multiplyColors(textColor, colorFullAlpha) : colorFullAlpha; + + final clippedFrameRect = FlxRect.get(); if (clipRect != null) - { - clippedFrameRect = clipRect.intersection(FlxRect.weak(0, 0, frameWidth, frameHeight)); - - if (clippedFrameRect.isEmpty) - return; - } + clipRect.intersection(FlxRect.weak(0, 0, frameWidth, frameHeight), clippedFrameRect) else - { - clippedFrameRect = FlxRect.get(0, 0, frameWidth, frameHeight); - } + clippedFrameRect.set(0, 0, frameWidth, frameHeight); + + if (clippedFrameRect.isEmpty) + return; final cameras = getCamerasLegacy(); for (camera in cameras) @@ -411,91 +368,42 @@ class FlxBitmapText extends FlxSprite if (background) { + final offsetX = _facingHorizontalMult != 1 ? frameWidth - origin.x : origin.x; + final offsetY = _facingVerticalMult != 1 ? frameHeight - origin.y : origin.y; // backround tile transformations - currFrame = FlxG.bitmap.whitePixel; + final currFrame = FlxG.bitmap.whitePixel; _matrix.identity(); _matrix.scale(0.1 * clippedFrameRect.width, 0.1 * clippedFrameRect.height); - _matrix.translate(clippedFrameRect.x - ox, clippedFrameRect.y - oy); - _matrix.scale(sx, sy); + _matrix.translate(clippedFrameRect.x - offsetX, clippedFrameRect.y - offsetY); + _matrix.scale(scale.x * _facingHorizontalMult, scale.y * _facingVerticalMult); if (angle != 0) { _matrix.rotateWithTrig(_cosAngle, _sinAngle); } - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(bgRed, bgGreen, bgBlue, bgAlpha); + _matrix.translate(_point.x + offsetX, _point.y + offsetY); + _colorParams.setMultipliersFromColor(bgColorMult); camera.drawPixels(currFrame, null, _matrix, _colorParams, blend, antialiasing); } - var hasColorOffsets:Bool = (colorTransform != null && colorTransform.hasRGBAOffsets()); - - drawItem = camera.startQuadBatch(font.parent, true, hasColorOffsets, blend, antialiasing, shader); - - for (j in 0...borderLength) - { - dataPos = j * 3; - - currFrame = font.getCharFrame(Std.int(borderDrawData[dataPos])); - - currTileX = borderDrawData[dataPos + 1]; - currTileY = borderDrawData[dataPos + 2]; - - if (clipRect != null) - { - clippedFrameRect.copyFrom(clipRect).offset(-currTileX, -currTileY); - currFrame = currFrame.clipTo(clippedFrameRect); - } - - currFrame.prepareMatrix(_matrix); - _matrix.translate(currTileX - ox, currTileY - oy); - _matrix.scale(sx, sy); - if (angle != 0) - { - _matrix.rotateWithTrig(_cosAngle, _sinAngle); - } - - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(borderRed, borderGreen, borderBlue, bAlpha); - drawItem.addQuad(currFrame, _matrix, _colorParams); - } - - for (j in 0...textLength) - { - dataPos = j * 3; - - currFrame = font.getCharFrame(Std.int(textDrawData[dataPos])); - - currTileX = textDrawData[dataPos + 1]; - currTileY = textDrawData[dataPos + 2]; - - if (clipRect != null) - { - clippedFrameRect.copyFrom(clipRect).offset(-currTileX, -currTileY); - currFrame = currFrame.clipTo(clippedFrameRect); - } - - currFrame.prepareMatrix(_matrix); - _matrix.translate(currTileX - ox, currTileY - oy); - _matrix.scale(sx, sy); - if (angle != 0) - { - _matrix.rotateWithTrig(_cosAngle, _sinAngle); - } - - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(textRed, textGreen, textBlue, tAlpha); - drawItem.addQuad(currFrame, _matrix, _colorParams); - } + final hasColorOffsets:Bool = (colorTransform != null && colorTransform.hasRGBAOffsets()); + final drawItem = camera.startQuadBatch(font.parent, true, hasColorOffsets, blend, antialiasing, shader); + + // draw the text border + batchDrawData(drawItem, borderDrawData, borderColorMult); + // draw the actual text + batchDrawData(drawItem, textDrawData, textColorMult); + #if FLX_DEBUG FlxBasic.visibleCount++; #end } - + // dispose clipRect helpers clippedFrameRect.put(); - + #if FLX_DEBUG if (FlxG.debugger.drawDebug) { @@ -504,6 +412,53 @@ class FlxBitmapText extends FlxSprite #end } } + + function batchDrawData(drawItem:FlxDrawQuadsItem, list:FlxBuffer, color:FlxColor) + { + final hex = color.toHexString(); + _colorParams.setMultipliersFromColor(color); + + final scaleX = scale.x * _facingHorizontalMult; + final scaleY = scale.y * _facingVerticalMult; + + final offsetX = _facingHorizontalMult != 1 ? frameWidth - origin.x : origin.x; + final offsetY = _facingVerticalMult != 1 ? frameHeight - origin.y : origin.y; + + for (item in list) + { + final currFrame = getClippedCharFrame(item); + currFrame.prepareMatrix(_matrix); + _matrix.translate(item.x - offsetX, item.y - offsetY); + _matrix.scale(scaleX, scaleY); + if (angle != 0) + { + _matrix.rotateWithTrig(_cosAngle, _sinAngle); + } + + _matrix.translate(_point.x + offsetX, _point.y + offsetY); + // TODO: cull offscreen chars + drawItem.addQuad(currFrame, _matrix, _colorParams); + } + } + + /** + * Gets the character frame and clips it according to the clipRect + */ + inline function getClippedCharFrame(item:TileDrawItem) + { + final currFrame = font.getCharFrame(item.charCode); + + return if (clipRect == null) + currFrame; + else + { + final clippedFrameRect = FlxRect.get().copyFrom(clipRect); + clippedFrameRect.offset(-item.x, -item.y); + final result = currFrame.clipTo(clippedFrameRect); + clippedFrameRect.put(); + result; + } + } override function set_clipRect(Rect:FlxRect):FlxRect { @@ -1088,25 +1043,20 @@ class FlxBitmapText extends FlxSprite } else if (FlxG.renderTile) { - textData.splice(0, textData.length); + textData.resize(0); } _fieldWidth = frameWidth; - var numLines:Int = _lines.length; - var line:UnicodeString; - var lineWidth:Int; - - var ox:Int, oy:Int; - + final numLines:Int = _lines.length; for (i in 0...numLines) { - line = _lines[i]; - lineWidth = _linesWidth[i]; + final line:UnicodeString = _lines[i]; + final lineWidth = _linesWidth[i]; // LEFT - ox = font.minOffsetX; - oy = i * (font.lineHeight + lineSpacing) + padding; + var ox = font.minOffsetX; + var oy = i * (font.lineHeight + lineSpacing) + padding; if (alignment == FlxTextAlign.CENTER) { @@ -1151,17 +1101,14 @@ class FlxBitmapText extends FlxSprite function blitLine(line:UnicodeString, startX:Int, startY:Int):Void { - var data:Array = []; + final data = new FlxBuffer(); addLineData(line, startX, startY, data); while (data.length > 0) { - final charCode = Std.int(data.shift()); - final x = data.shift(); - final y = data.shift(); - - final charFrame = font.getCharFrame(charCode); - _flashPoint.setTo(x, y); + final item = data.shift(); + final charFrame = font.getCharFrame(item.charCode); + _flashPoint.setTo(item.x, item.y); charFrame.paint(textBitmap, _flashPoint, true); } } @@ -1174,10 +1121,8 @@ class FlxBitmapText extends FlxSprite addLineData(line, startX, startY, textData); } - function addLineData(line:UnicodeString, startX:Int, startY:Int, data:Array) + function addLineData(line:UnicodeString, startX:Int, startY:Int, data:FlxBuffer) { - var pos:Int = data.length; - var curX:Float = startX; var curY:Int = startY; @@ -1202,9 +1147,7 @@ class FlxBitmapText extends FlxSprite final hasFrame = font.charExists(charCode); if (hasFrame && !isSpace) { - data[pos++] = charCode; - data[pos++] = curX; - data[pos++] = curY; + data.push(charCode, curX, curY); } if (hasFrame || isSpace) @@ -1278,8 +1221,8 @@ class FlxBitmapText extends FlxSprite } else { - textDrawData.splice(0, textDrawData.length); - borderDrawData.splice(0, borderDrawData.length); + textDrawData.resize(0); + borderDrawData.resize(0); } // use local var to avoid get_width and recursion @@ -1441,32 +1384,24 @@ class FlxBitmapText extends FlxSprite if (!FlxG.renderTile) return; - var data:Array = isFront ? textDrawData : borderDrawData; + final data = isFront ? textDrawData : borderDrawData; - var pos:Int = data.length; - var textPos:Int; - var textLen:Int = Std.int(textData.length / 3); - var rect = FlxRect.get(); - var frameVisible; + final rect = FlxRect.get(); + final textLen:Int = textData.length; for (i in 0...textLen) { - textPos = 3 * i; - - frameVisible = true; - + final textPos = i; + final item = textData.get(textPos); + if (clipRect != null) { - rect.copyFrom(clipRect).offset(-textData[textPos + 1] - posX, -textData[textPos + 2] - posY); - frameVisible = font.getCharFrame(Std.int(textData[textPos])).clipTo(rect).type != FlxFrameType.EMPTY; - } - - if (frameVisible) - { - data[pos++] = textData[textPos]; - data[pos++] = textData[textPos + 1] + posX; - data[pos++] = textData[textPos + 2] + posY; + rect.copyFrom(clipRect).offset(-item.x - posX, -item.y - posY); + if (font.getCharFrame(item.charCode).clipTo(rect).type == FlxFrameType.EMPTY) + continue; } + + data.push(item.charCodeRaw, item.x + posX, item.y + posY); } rect.put(); @@ -1805,6 +1740,14 @@ enum WordSplitConditions WIDTH(minPixels:Int); } +typedef TileDrawItemRaw = { final charCodeRaw:Float; final x:Float; final y:Float; }; +@:forward +abstract TileDrawItem(TileDrawItemRaw) from TileDrawItemRaw +{ + public var charCode(get, never):Int; + inline function get_charCode() return Std.int(this.charCodeRaw); +} + /* * TODO - enum WordSplitMethod: determines how words look when split, ex: * * whether split words start on a new line diff --git a/flixel/util/FlxColorTransformUtil.hx b/flixel/util/FlxColorTransformUtil.hx index c13f7b4e01..f66d3811e0 100644 --- a/flixel/util/FlxColorTransformUtil.hx +++ b/flixel/util/FlxColorTransformUtil.hx @@ -13,6 +13,15 @@ class FlxColorTransformUtil return transform; } + + /** + * Helper for transform.setMultipliers(color.redFloat, color.greenFloat, color.blueFloat, color.alphaFloat) + * @since 5.9.0 + */ + public static inline function setMultipliersFromColor(transform:ColorTransform, color:FlxColor):ColorTransform + { + return setMultipliers(transform, color.redFloat, color.greenFloat, color.blueFloat, color.alphaFloat); + } public static function setOffsets(transform:ColorTransform, red:Float, green:Float, blue:Float, alpha:Float):ColorTransform { @@ -23,7 +32,16 @@ class FlxColorTransformUtil return transform; } - + + /** + * Helper for transform.setOffsets(color.redFloat, color.greenFloat, color.blueFloat, color.alphaFloat) + * @since 5.9.0 + */ + public static inline function setOffsetsFromColor(transform:ColorTransform, color:FlxColor):ColorTransform + { + return setOffsets(transform, color.redFloat, color.greenFloat, color.blueFloat, color.alphaFloat); + } + /** * Returns whether red, green, or blue multipliers are set to anything other than 1. */ From 744a4b570f0acd0d4a6cb15c2f9b250bec4f817a Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 14:36:01 -0500 Subject: [PATCH 05/15] add FlxBufferArray, fix field order --- flixel/system/FlxBuffer.hx | 45 ++++++-- tests/unit/src/flixel/system/FlxBufferTest.hx | 103 +++++++++++++++++- 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx index f727a8311a..9c42e81627 100644 --- a/flixel/system/FlxBuffer.hx +++ b/flixel/system/FlxBuffer.hx @@ -10,13 +10,13 @@ using haxe.macro.Tools; class BufferMacro { - public static function build(includeGetters = true):ComplexType + public static function build(isVector:Bool, includeGetters = true):ComplexType { final local = Context.getLocalType(); switch local { // Extract the type parameter case TInst(local, [type]): - return buildBuffer(getFields(type, includeGetters), type); + return buildBuffer(getFields(type, includeGetters), type, isVector); default: throw "Expected TInst"; } @@ -53,14 +53,17 @@ class BufferMacro } } - static function buildBuffer(fields:Array, type:Type) + static function buildBuffer(fields:Array, type:Type, isVector:Bool) { + // Sort fields by pos to ensure order is maintained (weird that this is needed) + fields.sort((a, b)->a.pos.getInfos().max - b.pos.getInfos().max); + // Distinguish getters from actual fields final getters:Array = fields.copy(); var i = fields.length; while (i-- > 0) { - // todo: Prevent double adds for getters over + // TODO: Prevent double adds for getters over typedef fields? final field = fields[i]; if (field.kind.match(FVar(AccCall, _))) { @@ -68,15 +71,16 @@ class BufferMacro } } + // Generate unique name for each type final arrayType = fields[0].type; final arrayTypeName = getTypeName(arrayType); - final prefix = arrayTypeName + "_" + getTypeIdentifier(type); + final prefix = (isVector ? "" : "Array_") + arrayTypeName + "_" + getTypeIdentifier(type); final name = "Buffer_" + prefix; final iterName = "BufferIterator_" + prefix; final kvIterName = "BufferKeyValueIterator_" + prefix; final complexType = type.toComplexType(); - // First check whether the generated type already exists + // Check whether the generated type already exists try { Context.getType(name); @@ -92,6 +96,7 @@ class BufferMacro if (length < 2) throw "Just use an array"; + // Make sure all fields use the same type for (i in 1...length) { if (arrayType.toString() != fields[i].type.toString()) @@ -100,6 +105,7 @@ class BufferMacro } } + // An easy way to instantiate the type from an index final objectDecl:Expr = { pos: Context.currentPos(), @@ -114,6 +120,7 @@ class BufferMacro ]) } + // Make an overloaded `push` that takes an arg for each field final pushEachBody = [ for (field in fields) @@ -146,6 +153,7 @@ class BufferMacro }) } + // Make an overloaded `push` that takes an item instance final pushItemBody = [ for (field in fields) @@ -171,9 +179,11 @@ class BufferMacro }) } + // Get the iterator complex types (which are actually created later) final iterCt = { name: iterName, pack: [] }; final kvIterCt = { name: kvIterName, pack: [] }; + // define the buffer final def = macro class $name { static inline var FIELDS:Int = $v{length}; @@ -184,7 +194,7 @@ class BufferMacro public inline function new () { - this = new openfl.Vector(); + $b{[isVector ? macro this = new openfl.Vector() : macro this = [] ]} } /** Fetches the item at the desired index */ @@ -235,7 +245,10 @@ class BufferMacro */ public inline function resize(length:Int) { - this.length = length * FIELDS; + $e{isVector + ? macro this.length = length * FIELDS + : macro this.resize(length * FIELDS) + } } /** Returns an iterator of the buffer's items */ @@ -251,6 +264,7 @@ class BufferMacro } }; + // Add our overloaded push methods from before def.fields.push(pushEachFunc); def.fields.push(pushItemFunc); for (i => field in getters) @@ -269,8 +283,9 @@ class BufferMacro def.fields.push(getter); } + // Generate unique doc, but with static example final itemTypeName = getTypeName(type); - def.doc = 'An `openfl.Vector<$arrayTypeName>` disguised as an `Array<$itemTypeName>`.' + def.doc = 'An `${isVector ? "openfl.Vector" : "Array"}<$arrayTypeName>` disguised as an `Array<$itemTypeName>`.' + "\nOften used in under-the-hood Flixel systems, like rendering," + "\nwhere creating actual instances of objects every frame would balloon memory." + "\n" @@ -298,10 +313,14 @@ class BufferMacro + "\nfor that reason it is recommended to use final vars"; // `macro class` gives a TDClass, so that needs to be replaced final arrayCT = arrayType.toComplexType(); - def.kind = TDAbstract((macro:openfl.Vector<$arrayCT>), [macro:openfl.Vector<$arrayCT>], [macro:openfl.Vector<$arrayCT>]); + + // Determine our buffer's base + final listType = (isVector ? macro:openfl.Vector<$arrayCT> : macro:Array<$arrayCT>); + def.kind = TDAbstract(listType, [listType], [listType]); Context.defineType(def); final bufferCt = Context.getType(name).toComplexType(); + // Make our iterator final iterDef = macro class $iterName { var list:$bufferCt; @@ -330,6 +349,7 @@ class BufferMacro // iterDef.kind = TDClass(null, [], false, false, false); Context.defineType(iterDef); + // Make our key-value iterator final kvIterDef = macro class $kvIterName { var list:$bufferCt; @@ -387,6 +407,9 @@ class BufferMacro } #else -@:genericBuild(flixel.system.FlxBuffer.BufferMacro.build()) +@:genericBuild(flixel.system.FlxBuffer.BufferMacro.build(true)) class FlxBuffer {} + +@:genericBuild(flixel.system.FlxBuffer.BufferMacro.build(false)) +class FlxBufferArray {} #end \ No newline at end of file diff --git a/tests/unit/src/flixel/system/FlxBufferTest.hx b/tests/unit/src/flixel/system/FlxBufferTest.hx index 2129ceb667..b7fe77db80 100644 --- a/tests/unit/src/flixel/system/FlxBufferTest.hx +++ b/tests/unit/src/flixel/system/FlxBufferTest.hx @@ -1,11 +1,12 @@ package flixel.system; +import flixel.system.FlxBuffer; import massive.munit.Assert; class FlxBufferTest { @Test - function testAll() + function testVector() { final bufferStr = new FlxBuffer<{ x:String, y:String }>(); for (i in 0...100) @@ -45,6 +46,106 @@ class FlxBufferTest Assert.areEqual(0, buffer.shift().sum); Assert.areEqual(18, buffer.pop().sum); } + + @Test + function testArray() + { + final bufferStr = new FlxBufferArray<{ x:String, y:String }>(); + for (i in 0...100) + bufferStr.push({ x: Std.string(i % 10), y: Std.string(Std.int(i / 10)) }); + + bufferStr.shift(); + bufferStr.pop(); + + // make sure it compiles + for (i=>item in bufferStr) + { + item; + i; + } + + final bufferTD = new FlxBufferArray<{ x:Float, y:Float }>(); + final bufferTD2 = new FlxBufferArray(); + Assert.areEqual(1, bufferTD.push(5, 10)); + Assert.areEqual(5, bufferTD.getX(0)); + Assert.areEqual(10, bufferTD.getY(0)); + + final buffer = new FlxBufferArray(); + for (i in 0...100) + buffer.push({ x: i % 10, y: Std.int(i / 10) }); + + Assert.areEqual(5, buffer.getX(15)); + Assert.areEqual(1, buffer.getY(15)); + Assert.areEqual(6, buffer.getSum(15)); + + // make sure it compiles + for (i=>item in buffer) + { + item; + i; + } + + Assert.areEqual(0, buffer.shift().sum); + Assert.areEqual(18, buffer.pop().sum); + } + + @Test + function testArgOrderVector() + { + final buffer = new FlxBuffer<{ d:Int, c:Int, b:Int, a:Int }>(); + buffer.push(0, 10, 20, 30); + Assert.areEqual( 0, buffer.getD(0)); + Assert.areEqual(10, buffer.getC(0)); + Assert.areEqual(20, buffer.getB(0)); + Assert.areEqual(30, buffer.getA(0)); + + buffer.push({ d:5, c:15, b:25, a:35 }); + Assert.areEqual( 5, buffer.getD(1)); + Assert.areEqual(15, buffer.getC(1)); + Assert.areEqual(25, buffer.getB(1)); + Assert.areEqual(35, buffer.getA(1)); + + buffer.push({ a:6, b:16, c:26, d:36 }); + Assert.areEqual( 6, buffer.getA(2)); + Assert.areEqual(16, buffer.getB(2)); + Assert.areEqual(26, buffer.getC(2)); + Assert.areEqual(36, buffer.getD(2)); + + final item = buffer.get(2); + Assert.areEqual( 6, item.a); + Assert.areEqual(16, item.b); + Assert.areEqual(26, item.c); + Assert.areEqual(36, item.d); + } + + @Test + function testArgOrderArray() + { + final buffer = new FlxBufferArray<{ d:Int, c:Int, b:Int, a:Int }>(); + buffer.push(0, 10, 20, 30); + Assert.areEqual( 0, buffer.getD(0)); + Assert.areEqual(10, buffer.getC(0)); + Assert.areEqual(20, buffer.getB(0)); + Assert.areEqual(30, buffer.getA(0)); + + buffer.push({ d:5, c:15, b:25, a:35 }); + Assert.areEqual( 5, buffer.getD(1)); + Assert.areEqual(15, buffer.getC(1)); + Assert.areEqual(25, buffer.getB(1)); + Assert.areEqual(35, buffer.getA(1)); + + buffer.push({ a:6, b:16, c:26, d:36 }); + Assert.areEqual( 6, buffer.getA(2)); + Assert.areEqual(16, buffer.getB(2)); + Assert.areEqual(26, buffer.getC(2)); + Assert.areEqual(36, buffer.getD(2)); + + final item = buffer.get(2); + Assert.areEqual( 6, item.a); + Assert.areEqual(16, item.b); + Assert.areEqual(26, item.c); + Assert.areEqual(36, item.d); + } } typedef XY = { x:Float, y:Float } From 7a42ef44eef497f5a7f8665e5f7e8a222cc97542 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 14:58:40 -0500 Subject: [PATCH 06/15] fix ci --- flixel/text/FlxBitmapText.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index c69901f9f3..1cd7543661 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -321,7 +321,7 @@ class FlxBitmapText extends FlxSprite { checkPendingChanges(true); - final FULL = 0xFF*0xFF; + final full = 0xFF*0xFF; function multiplyColors(a:FlxColor, b:FlxColor) { final aR = StringTools.hex(a.red); @@ -332,7 +332,7 @@ class FlxBitmapText extends FlxSprite final bB = StringTools.hex(b.blue); final aA = StringTools.hex(a.alpha); final bA = StringTools.hex(b.alpha); - return FlxColor.fromRGBFloat(a.red * b.red / FULL, a.green * b.green / FULL, a.blue * b.blue / FULL, a.alpha * b.alpha / FULL); + return FlxColor.fromRGBFloat(a.red * b.red / full, a.green * b.green / full, a.blue * b.blue / full, a.alpha * b.alpha / full); } final colorFullAlpha = color.rgb | 0xFF000000; From 55d7eebc38bb58117c0c99542672e5852d2b55de Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 15:00:12 -0500 Subject: [PATCH 07/15] remove debugging code --- flixel/text/FlxBitmapText.hx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index 1cd7543661..b0e3d45d69 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -324,14 +324,6 @@ class FlxBitmapText extends FlxSprite final full = 0xFF*0xFF; function multiplyColors(a:FlxColor, b:FlxColor) { - final aR = StringTools.hex(a.red); - final bR = StringTools.hex(b.red); - final aG = StringTools.hex(a.green); - final bG = StringTools.hex(b.green); - final aB = StringTools.hex(a.blue); - final bB = StringTools.hex(b.blue); - final aA = StringTools.hex(a.alpha); - final bA = StringTools.hex(b.alpha); return FlxColor.fromRGBFloat(a.red * b.red / full, a.green * b.green / full, a.blue * b.blue / full, a.alpha * b.alpha / full); } @@ -415,7 +407,6 @@ class FlxBitmapText extends FlxSprite function batchDrawData(drawItem:FlxDrawQuadsItem, list:FlxBuffer, color:FlxColor) { - final hex = color.toHexString(); _colorParams.setMultipliersFromColor(color); final scaleX = scale.x * _facingHorizontalMult; From 1f3555675b6c2aafa043f50d272faa64b9e61146 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 16:18:25 -0500 Subject: [PATCH 08/15] fix coverage triangles --- flixel/text/FlxBitmapText.hx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index b0e3d45d69..d4235e3ccb 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -7,7 +7,11 @@ import flixel.FlxSprite; import openfl.geom.ColorTransform; import flixel.graphics.frames.FlxBitmapFont; import flixel.graphics.frames.FlxFrame; +#if FLX_RENDER_TRIANGLE +import flixel.graphics.tile.FlxDrawTrianglesItem; +#else import flixel.graphics.tile.FlxDrawQuadsItem; +#end import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.text.FlxText.FlxTextAlign; @@ -405,7 +409,11 @@ class FlxBitmapText extends FlxSprite } } + #if FLX_RENDER_TRIANGLE + function batchDrawData(drawItem:FlxDrawTrianglesItem, list:FlxBuffer, color:FlxColor) + #else function batchDrawData(drawItem:FlxDrawQuadsItem, list:FlxBuffer, color:FlxColor) + #end { _colorParams.setMultipliersFromColor(color); From 5e723d5e340abefa2cf5f4875dde59c36767db41 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 19:42:51 -0500 Subject: [PATCH 09/15] simplfy args --- flixel/system/FlxBuffer.hx | 92 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx index 9c42e81627..88c45ecf51 100644 --- a/flixel/system/FlxBuffer.hx +++ b/flixel/system/FlxBuffer.hx @@ -120,15 +120,6 @@ class BufferMacro ]) } - // Make an overloaded `push` that takes an arg for each field - final pushEachBody = - [ - for (field in fields) - { - macro this.push($i{field.name}); - } - ]; - pushEachBody.push(macro return length); final pushEachFunc:Field = { doc:"Creates an item with the given values and adds it at the end of this Buffer and returns the new length of this Array.\n\n" @@ -139,43 +130,14 @@ class BufferMacro access: [APublic, AOverload, AExtern, AInline], kind:FFun ({ - args: - [ - for (field in fields) + args: fields.map(function (f):FunctionArg return { type: f.type.toComplexType(), name: f.name }), + expr:macro { + $b{[for (field in fields) { - { - type: field.type.toComplexType(), - name: field.name - } - } - ], - expr:macro $b{pushEachBody} - }) - } - - // Make an overloaded `push` that takes an item instance - final pushItemBody = - [ - for (field in fields) - { - final name = field.name; - macro this.push(item.$name); - } - ]; - pushItemBody.push(macro return length); - - final pushItemFunc:Field = - { - doc:"Adds the item at the end of this Buffer and returns the new length of this Array.\n\n" - + "This operation modifies this Array in place.\n\n" - + "this.length increases by 1.", - pos: Context.currentPos(), - name: "push", - access: [APublic, AOverload, AExtern, AInline], - kind:FFun - ({ - args: [{name: "item", type: complexType}], - expr:macro $b{pushItemBody} + macro this.push($i{field.name}); + }]} + return length; + } }) } @@ -194,7 +156,10 @@ class BufferMacro public inline function new () { - $b{[isVector ? macro this = new openfl.Vector() : macro this = [] ]} + $e{ isVector + ? macro this = new openfl.Vector() + : macro this = [] + } } /** Fetches the item at the desired index */ @@ -205,6 +170,39 @@ class BufferMacro return $e{objectDecl}; } + /** + * Adds the item at the end of this Buffer and returns the new length of this Array + * + * This operation modifies this Array in place + * + * `this.length` increases by `1` + */ + public overload extern inline function push(item:$complexType):Int + { + $b{[ + for (field in fields) + { + final name = field.name; + macro this.push(item.$name); + } + ]} + return length; + } + + + /** + * Creates an item with the given values and adds it at the end of this Buffer and returns the new length of this Array + * + * This operation modifies this Array in place + * + * `this.length` increases by `1` + */ + // public overload extern inline function push($a{???}):Int + // { + // $b{[fields.map((f)->macro this.push($i{f.name}))]} + // return length; + // } + /** Removes and returns the first item */ public inline function shift():$complexType { @@ -266,7 +264,7 @@ class BufferMacro // Add our overloaded push methods from before def.fields.push(pushEachFunc); - def.fields.push(pushItemFunc); + // def.fields.push(pushItemFunc); for (i => field in getters) { final getter:Field = From e3e4f1719280eda51f4170717122739645958190 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 19:43:21 -0500 Subject: [PATCH 10/15] use buffers in draw quads --- flixel/graphics/tile/FlxDrawQuadsItem.hx | 106 ++++++++++++++--------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/flixel/graphics/tile/FlxDrawQuadsItem.hx b/flixel/graphics/tile/FlxDrawQuadsItem.hx index bd73f8b346..3a70702402 100644 --- a/flixel/graphics/tile/FlxDrawQuadsItem.hx +++ b/flixel/graphics/tile/FlxDrawQuadsItem.hx @@ -2,44 +2,88 @@ package flixel.graphics.tile; import flixel.FlxCamera; import flixel.graphics.frames.FlxFrame; -import flixel.graphics.tile.FlxDrawBaseItem.FlxDrawItemType; -import flixel.system.FlxAssets.FlxShader; +import flixel.graphics.tile.FlxDrawBaseItem; +import flixel.system.FlxAssets; +import flixel.system.FlxBuffer; import flixel.math.FlxMatrix; +import flixel.math.FlxRect; import openfl.geom.ColorTransform; import openfl.display.ShaderParameter; import openfl.Vector; +typedef QuadRectRaw = { x:Float, y:Float, width:Float, height:Float }; +@:forward +abstract QuadRect(QuadRectRaw) from QuadRectRaw to QuadRectRaw +{ + @:from + static public inline function fromFlxRect(rect:FlxRect):QuadRect + { + return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; + } + + @:from + static public inline function fromRect(rect:openfl.geom.Rectangle):QuadRect + { + return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; + } + + public inline function toFlxRect(rect:FlxRect):FlxRect + { + return rect.set(this.x, this.y, this.width, this.height); + } +} + +typedef QuadTransformRaw = { a:Float, b:Float, c:Float, d:Float, tx:Float, ty:Float }; +@:forward +abstract QuadTransform(QuadTransformRaw) from QuadTransformRaw to QuadTransformRaw +{ + @:from + static public inline function fromMatrix(matrix:FlxMatrix):QuadTransform + { + return { a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, tx: matrix.tx, ty: matrix.ty }; + } + + public inline function toMatrix(matrix:FlxMatrix):FlxMatrix + { + matrix.setTo(this.a, this.b, this.c, this.d, this.tx, this.ty); + return matrix; + } +} + +typedef QuadColorMult = { r:Float, g:Float, b:Float, a:Float }; +typedef QuadColorOffset = { r:Float, g:Float, b:Float, a:Float }; + class FlxDrawQuadsItem extends FlxDrawBaseItem { static inline var VERTICES_PER_QUAD = #if (openfl >= "8.5.0") 4 #else 6 #end; public var shader:FlxShader; - var rects:Vector; - var transforms:Vector; + var rects:FlxBuffer; + var transforms:FlxBuffer; var alphas:Array; - var colorMultipliers:Array; - var colorOffsets:Array; + var colorMultipliers:FlxBufferArray; + var colorOffsets:FlxBufferArray; public function new() { super(); type = FlxDrawItemType.TILES; - rects = new Vector(); - transforms = new Vector(); + rects = new FlxBuffer(); + transforms = new FlxBuffer(); alphas = []; } override public function reset():Void { super.reset(); - rects.length = 0; - transforms.length = 0; - alphas.splice(0, alphas.length); + rects.resize(0); + transforms.resize(0); + alphas.resize(0); if (colorMultipliers != null) - colorMultipliers.splice(0, colorMultipliers.length); + colorMultipliers.resize(0); if (colorOffsets != null) - colorOffsets.splice(0, colorOffsets.length); + colorOffsets.resize(0); } override public function dispose():Void @@ -54,18 +98,8 @@ class FlxDrawQuadsItem extends FlxDrawBaseItem override public function addQuad(frame:FlxFrame, matrix:FlxMatrix, ?transform:ColorTransform):Void { - var rect = frame.frame; - rects.push(rect.x); - rects.push(rect.y); - rects.push(rect.width); - rects.push(rect.height); - - transforms.push(matrix.a); - transforms.push(matrix.b); - transforms.push(matrix.c); - transforms.push(matrix.d); - transforms.push(matrix.tx); - transforms.push(matrix.ty); + rects.push(frame.frame); + transforms.push(matrix); var alphaMultiplier = transform != null ? transform.alphaMultiplier : 1.0; for (i in 0...VERTICES_PER_QUAD) @@ -83,28 +117,14 @@ class FlxDrawQuadsItem extends FlxDrawBaseItem { if (transform != null) { - colorMultipliers.push(transform.redMultiplier); - colorMultipliers.push(transform.greenMultiplier); - colorMultipliers.push(transform.blueMultiplier); - - colorOffsets.push(transform.redOffset); - colorOffsets.push(transform.greenOffset); - colorOffsets.push(transform.blueOffset); - colorOffsets.push(transform.alphaOffset); + colorMultipliers.push(transform.redMultiplier, transform.greenMultiplier, transform.blueMultiplier, 1); + colorOffsets.push(transform.redOffset, transform.greenOffset, transform.blueOffset, transform.alphaOffset); } else { - colorMultipliers.push(1); - colorMultipliers.push(1); - colorMultipliers.push(1); - - colorOffsets.push(0); - colorOffsets.push(0); - colorOffsets.push(0); - colorOffsets.push(0); + colorMultipliers.push(1, 1, 1, 1); + colorOffsets.push(0, 0, 0, 0); } - - colorMultipliers.push(1); } } } From 1a058c2ad8925cc7b14f9c320d678cbc54b30cd1 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 21 Jun 2024 19:52:48 -0500 Subject: [PATCH 11/15] fix CC --- flixel/graphics/tile/FlxDrawQuadsItem.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flixel/graphics/tile/FlxDrawQuadsItem.hx b/flixel/graphics/tile/FlxDrawQuadsItem.hx index 3a70702402..ec5088b5cd 100644 --- a/flixel/graphics/tile/FlxDrawQuadsItem.hx +++ b/flixel/graphics/tile/FlxDrawQuadsItem.hx @@ -16,13 +16,13 @@ typedef QuadRectRaw = { x:Float, y:Float, width:Float, height:Float }; abstract QuadRect(QuadRectRaw) from QuadRectRaw to QuadRectRaw { @:from - static public inline function fromFlxRect(rect:FlxRect):QuadRect + public static inline function fromFlxRect(rect:FlxRect):QuadRect { return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; } @:from - static public inline function fromRect(rect:openfl.geom.Rectangle):QuadRect + public static inline function fromRect(rect:openfl.geom.Rectangle):QuadRect { return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; } @@ -38,7 +38,7 @@ typedef QuadTransformRaw = { a:Float, b:Float, c:Float, d:Float, tx:Float, ty:Fl abstract QuadTransform(QuadTransformRaw) from QuadTransformRaw to QuadTransformRaw { @:from - static public inline function fromMatrix(matrix:FlxMatrix):QuadTransform + public static inline function fromMatrix(matrix:FlxMatrix):QuadTransform { return { a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d, tx: matrix.tx, ty: matrix.ty }; } From 238d137d6404aa53f0a0656753f7fb7c4d8b21b3 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Sat, 22 Jun 2024 11:42:26 -0500 Subject: [PATCH 12/15] doc --- flixel/system/FlxBuffer.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx index 88c45ecf51..640436e744 100644 --- a/flixel/system/FlxBuffer.hx +++ b/flixel/system/FlxBuffer.hx @@ -308,7 +308,9 @@ class BufferMacro + "\nfeatures like `insert` and setting via array access operator, these can be" + "\nimplemented but are low priority" + "\n- Editing items retrieved from the buffer will not edit the corresponding indicies," - + "\nfor that reason it is recommended to use final vars"; + + "\nfor that reason it is recommended to use final vars" + + "\n- all retrieved items must be handled via inline functions to avoid ever actually" + + "\ninstantiating an anonymous structure. This includes `Std.string(item)`"; // `macro class` gives a TDClass, so that needs to be replaced final arrayCT = arrayType.toComplexType(); From b117fe0af19eb7afd896544be268e593bdc52177 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Sun, 23 Jun 2024 23:11:30 -0500 Subject: [PATCH 13/15] reorg types --- flixel/system/FlxBuffer.hx | 48 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx index 640436e744..2d0f5bbb17 100644 --- a/flixel/system/FlxBuffer.hx +++ b/flixel/system/FlxBuffer.hx @@ -141,9 +141,12 @@ class BufferMacro }) } + final bufferCt = TPath({pack: [], name: name}); // Get the iterator complex types (which are actually created later) final iterCt = { name: iterName, pack: [] }; final kvIterCt = { name: kvIterName, pack: [] }; + final itemTypeName = getTypeName(type); + final arrayCT = arrayType.toComplexType(); // define the buffer final def = macro class $name @@ -189,7 +192,6 @@ class BufferMacro return length; } - /** * Creates an item with the given values and adds it at the end of this Buffer and returns the new length of this Array * @@ -262,27 +264,7 @@ class BufferMacro } }; - // Add our overloaded push methods from before - def.fields.push(pushEachFunc); - // def.fields.push(pushItemFunc); - for (i => field in getters) - { - final getter:Field = - { - pos: Context.currentPos(), - name: "get" + field.name.charAt(0).toUpperCase() + field.name.substr(1), - access: [APublic, AInline], - kind:FFun - ({ - args: [{name: "index", type: (macro:Int)}], - expr:macro this[index * FIELDS + $v{i}] - }) - }; - def.fields.push(getter); - } - // Generate unique doc, but with static example - final itemTypeName = getTypeName(type); def.doc = 'An `${isVector ? "openfl.Vector" : "Array"}<$arrayTypeName>` disguised as an `Array<$itemTypeName>`.' + "\nOften used in under-the-hood Flixel systems, like rendering," + "\nwhere creating actual instances of objects every frame would balloon memory." @@ -311,15 +293,31 @@ class BufferMacro + "\nfor that reason it is recommended to use final vars" + "\n- all retrieved items must be handled via inline functions to avoid ever actually" + "\ninstantiating an anonymous structure. This includes `Std.string(item)`"; - // `macro class` gives a TDClass, so that needs to be replaced - final arrayCT = arrayType.toComplexType(); + // Add our overloaded push methods from before + def.fields.push(pushEachFunc); + + for (i => field in getters) + { + final fieldName = field.name; + final funcName = "get" + fieldName.charAt(0).toUpperCase() + fieldName.substr(1); + // Create the field in another class (for easy reification) then move it over + def.fields.push((macro class TempClass + { + /** Helper for `get(item).$name` */ + public inline function $funcName(index:Int) + { + return get(index).$fieldName; + } + }).fields[0]); + } + + // `macro class` gives a TDClass, so that needs to be replaced // Determine our buffer's base final listType = (isVector ? macro:openfl.Vector<$arrayCT> : macro:Array<$arrayCT>); def.kind = TDAbstract(listType, [listType], [listType]); Context.defineType(def); - final bufferCt = Context.getType(name).toComplexType(); // Make our iterator final iterDef = macro class $iterName { @@ -380,7 +378,7 @@ class BufferMacro Context.defineType(kvIterDef); // Return a `ComplexType` for the generated type - return TPath({pack: [], name: name}); + return bufferCt; } static function getTypeIdentifier(type:Type) From 24fe649427ae3b630244bc6e3915e721aa2cbedc Mon Sep 17 00:00:00 2001 From: George FunBook Date: Mon, 24 Jun 2024 14:40:56 -0500 Subject: [PATCH 14/15] add set, insert and slice --- flixel/system/FlxBuffer.hx | 65 ++++++++ tests/unit/src/FlxAssert.hx | 9 ++ tests/unit/src/flixel/system/FlxBufferTest.hx | 151 ++++++++++++------ 3 files changed, 173 insertions(+), 52 deletions(-) diff --git a/flixel/system/FlxBuffer.hx b/flixel/system/FlxBuffer.hx index 2d0f5bbb17..1f5442eb89 100644 --- a/flixel/system/FlxBuffer.hx +++ b/flixel/system/FlxBuffer.hx @@ -173,6 +173,20 @@ class BufferMacro return $e{objectDecl}; } + /** Fetches the item at the desired index */ + @:arrayAccess + public inline function set(pos:Int, item:$complexType):$complexType + { + $b{[ + for (i=>field in fields) + { + final name = field.name; + macro this[pos * FIELDS + $v{i}] = item.$name; + } + ]} + return item; + } + /** * Adds the item at the end of this Buffer and returns the new length of this Array * @@ -233,6 +247,36 @@ class BufferMacro return item; } + /** + * Inserts the element x at the position pos. + * + * This operation modifies this Array in place. + * + * The offset is calculated like so: + * + * If pos exceeds this.length, the offset is this.length. + * If pos is negative, the offset is calculated from the end of this Array, i.e. this.length + pos. If this yields a negative value, the offset is 0. + * Otherwise, the offset is pos. + * If the resulting offset does not exceed this.length, all elements from and including that offset to the end of this Array are moved one index ahead. + */ + public inline function insert(pos:Int, item:$complexType) + { + $b{[ + for (i=>field in fields) + { + final name = field.name; + if (isVector) + { + macro this.insertAt(pos * FIELDS + $v{i}, item.$name); + } + else + { + macro this.insert(pos * FIELDS + $v{i}, item.$name); + } + } + ]} + } + /** * Set the length of the Array. * If len is shorter than the array's current size, the last length - len elements will @@ -251,6 +295,27 @@ class BufferMacro } } + /** + * Creates a shallow copy of the range of this Buffer, starting at and including pos, + * up to but not including end. + * + * This operation does not modify this Buffer. + * + * The elements are not copied and retain their identity. + * + * If end is omitted or exceeds this.length, it defaults to the end of this Buffer. + * + * If pos or end are negative, their offsets are calculated from the end of this + * Buffer by this.length + pos and this.length + end respectively. If this yields + * a negative value, 0 is used instead. + * + * If pos exceeds this.length or if end is less than or equals pos, the result is []. + */ + public inline function slice(pos:Int, ?end:Int):$bufferCt + { + return this.slice(pos * FIELDS, (end != null ? end : length) * FIELDS); + } + /** Returns an iterator of the buffer's items */ public inline function iterator() { diff --git a/tests/unit/src/FlxAssert.hx b/tests/unit/src/FlxAssert.hx index 1c501493ac..b083700050 100644 --- a/tests/unit/src/FlxAssert.hx +++ b/tests/unit/src/FlxAssert.hx @@ -124,4 +124,13 @@ class FlxAssert else Assert.fail('Value [$actual] is not within [$margin] of [( x:$expectedX | y:$expectedY )]', info); } + + public static function allEqual(expected:T, results:Array, ?msg:String, ?info:PosInfos) + { + for (i=>actual in results) + { + final message = msg != null ? msg : 'Value $i [$actual] was not equal to expected value [$expected]'; + Assert.areEqual(expected, actual, msg, info); + } + } } diff --git a/tests/unit/src/flixel/system/FlxBufferTest.hx b/tests/unit/src/flixel/system/FlxBufferTest.hx index b7fe77db80..eb54bba66c 100644 --- a/tests/unit/src/flixel/system/FlxBufferTest.hx +++ b/tests/unit/src/flixel/system/FlxBufferTest.hx @@ -36,6 +36,11 @@ class FlxBufferTest Assert.areEqual(1, buffer.getY(15)); Assert.areEqual(6, buffer.getSum(15)); + buffer.set(0, { x: 1000, y: 1000 }); + FlxAssert.allEqual(2000.0, [buffer.getSum(0), buffer.get(0).sum]); + buffer.insert(1, { x: 500, y: 500 }); + FlxAssert.allEqual(1000.0, [buffer.getSum(1), buffer.get(1).sum]); + // make sure it compiles for (i=>item in buffer) { @@ -43,7 +48,7 @@ class FlxBufferTest i; } - Assert.areEqual(0, buffer.shift().sum); + Assert.areEqual(2000, buffer.shift().sum); Assert.areEqual(18, buffer.pop().sum); } @@ -78,6 +83,11 @@ class FlxBufferTest Assert.areEqual(1, buffer.getY(15)); Assert.areEqual(6, buffer.getSum(15)); + buffer.set(0, { x: 1000, y: 1000 }); + FlxAssert.allEqual(2000.0, [buffer.getSum(0), buffer.get(0).sum]); + buffer.insert(1, { x: 500, y: 500 }); + FlxAssert.allEqual(1000.0, [buffer.getSum(1), buffer.get(1).sum]); + // make sure it compiles for (i=>item in buffer) { @@ -85,66 +95,103 @@ class FlxBufferTest i; } - Assert.areEqual(0, buffer.shift().sum); + Assert.areEqual(2000, buffer.shift().sum); Assert.areEqual(18, buffer.pop().sum); } @Test - function testArgOrderVector() + function testArgOrder() { - final buffer = new FlxBuffer<{ d:Int, c:Int, b:Int, a:Int }>(); - buffer.push(0, 10, 20, 30); - Assert.areEqual( 0, buffer.getD(0)); - Assert.areEqual(10, buffer.getC(0)); - Assert.areEqual(20, buffer.getB(0)); - Assert.areEqual(30, buffer.getA(0)); - - buffer.push({ d:5, c:15, b:25, a:35 }); - Assert.areEqual( 5, buffer.getD(1)); - Assert.areEqual(15, buffer.getC(1)); - Assert.areEqual(25, buffer.getB(1)); - Assert.areEqual(35, buffer.getA(1)); - - buffer.push({ a:6, b:16, c:26, d:36 }); - Assert.areEqual( 6, buffer.getA(2)); - Assert.areEqual(16, buffer.getB(2)); - Assert.areEqual(26, buffer.getC(2)); - Assert.areEqual(36, buffer.getD(2)); - - final item = buffer.get(2); - Assert.areEqual( 6, item.a); - Assert.areEqual(16, item.b); - Assert.areEqual(26, item.c); - Assert.areEqual(36, item.d); + final bVector = new FlxBuffer<{ d:Int, c:Int, b:Int, a:Int }>(); + final bArray = new FlxBufferArray<{ d:Int, c:Int, b:Int, a:Int }>(); + + bVector.push(0, 10, 20, 30); + bArray.push(0, 10, 20, 30); + FlxAssert.allEqual( 0.0, [ bVector.getD(0), bVector.get(0).d, bArray.getD(0), bArray.get(0).d]); + FlxAssert.allEqual(10.0, [ bVector.getC(0), bVector.get(0).c, bArray.getC(0), bArray.get(0).c]); + FlxAssert.allEqual(20.0, [ bVector.getB(0), bVector.get(0).b, bArray.getB(0), bArray.get(0).b]); + FlxAssert.allEqual(30.0, [ bVector.getA(0), bVector.get(0).a, bArray.getA(0), bArray.get(0).a]); + + bVector.push({ d:5, c:15, b:25, a:35 }); + bArray.push({ d:5, c:15, b:25, a:35 }); + FlxAssert.allEqual( 5.0, [ bVector.getD(1), bVector.get(1).d, bArray.getD(1), bArray.get(1).d]); + FlxAssert.allEqual(15.0, [ bVector.getC(1), bVector.get(1).c, bArray.getC(1), bArray.get(1).c]); + FlxAssert.allEqual(25.0, [ bVector.getB(1), bVector.get(1).b, bArray.getB(1), bArray.get(1).b]); + FlxAssert.allEqual(35.0, [ bVector.getA(1), bVector.get(1).a, bArray.getA(1), bArray.get(1).a]); + + bVector.insert(1, { d:7, c:17, b:27, a:37 }); + bArray.insert(1, { d:7, c:17, b:27, a:37 }); + FlxAssert.allEqual( 7.0, [ bVector.getD(1), bVector.get(1).d, bArray.getD(1), bArray.get(1).d]); + FlxAssert.allEqual(17.0, [ bVector.getC(1), bVector.get(1).c, bArray.getC(1), bArray.get(1).c]); + FlxAssert.allEqual(27.0, [ bVector.getB(1), bVector.get(1).b, bArray.getB(1), bArray.get(1).b]); + FlxAssert.allEqual(37.0, [ bVector.getA(1), bVector.get(1).a, bArray.getA(1), bArray.get(1).a]); + + bVector.set(2, { d:8, c:18, b:28, a:38 }); + bArray.set(2, { d:8, c:18, b:28, a:38 }); + FlxAssert.allEqual( 8.0, [ bVector.getD(2), bVector.get(2).d, bArray.getD(2), bArray.get(2).d]); + FlxAssert.allEqual(18.0, [ bVector.getC(2), bVector.get(2).c, bArray.getC(2), bArray.get(2).c]); + FlxAssert.allEqual(28.0, [ bVector.getB(2), bVector.get(2).b, bArray.getB(2), bArray.get(2).b]); + FlxAssert.allEqual(38.0, [ bVector.getA(2), bVector.get(2).a, bArray.getA(2), bArray.get(2).a]); } @Test - function testArgOrderArray() + function testSlice() + { + final bVector = new FlxBuffer(); + final bArray = new FlxBufferArray(); + final array = new Array(); + for (i in 0...100) + { + final item:XYAbs = { x: i % 10, y: Std.int(i / 10) }; + bVector.push(item); + bArray.push(item); + array.push(item); + } + + FlxAssert.allEqual(100, + [ bVector.length, bVector.slice(0).length + , bArray.length, bArray.slice(0).length + , array.length, array.slice(0).length + ]); + FlxAssert.allEqual(900.0, + [ getTotalSum(bVector), getTotalSum(bVector.slice(0)) + , getTotalSum( bArray), getTotalSum( bArray.slice(0)) + , getTotalSum( array), getTotalSum( array.slice(0)) + ]); + FlxAssert.allEqual(575.0, + [ getTotalSum(bVector.slice(50)) + , getTotalSum( bArray.slice(50)) + , getTotalSum( array.slice(50)) + ]); + FlxAssert.allEqual(450.0, + [ getTotalSum(bVector.slice(25, 75)), getTotalSum(bVector.slice(25, -25)) + , getTotalSum( bArray.slice(25, 75)), getTotalSum( bArray.slice(25, -25)) + , getTotalSum( array.slice(25, 75)), getTotalSum( array.slice(25, -25)) + ]); + } + + public overload extern inline function getTotalSum(buffer:FlxBuffer) + { + return getTotalSum((cast buffer.iterator():Iterator)); + } + + public overload extern inline function getTotalSum(buffer:FlxBufferArray) + { + return getTotalSum((cast buffer.iterator():Iterator)); + } + + public overload extern inline function getTotalSum(array:Array) + { + return getTotalSum(array.iterator()); + } + + public overload extern inline function getTotalSum(iter:Iterator) { - final buffer = new FlxBufferArray<{ d:Int, c:Int, b:Int, a:Int }>(); - buffer.push(0, 10, 20, 30); - Assert.areEqual( 0, buffer.getD(0)); - Assert.areEqual(10, buffer.getC(0)); - Assert.areEqual(20, buffer.getB(0)); - Assert.areEqual(30, buffer.getA(0)); - - buffer.push({ d:5, c:15, b:25, a:35 }); - Assert.areEqual( 5, buffer.getD(1)); - Assert.areEqual(15, buffer.getC(1)); - Assert.areEqual(25, buffer.getB(1)); - Assert.areEqual(35, buffer.getA(1)); - - buffer.push({ a:6, b:16, c:26, d:36 }); - Assert.areEqual( 6, buffer.getA(2)); - Assert.areEqual(16, buffer.getB(2)); - Assert.areEqual(26, buffer.getC(2)); - Assert.areEqual(36, buffer.getD(2)); - - final item = buffer.get(2); - Assert.areEqual( 6, item.a); - Assert.areEqual(16, item.b); - Assert.areEqual(26, item.c); - Assert.areEqual(36, item.d); + // return Lambda.fold(buffer, (item, total)->total + item.sum, 0); + var total = 0.0; + for (item in iter) + total += item.sum; + return total; } } From ff3334d7be89b91f1cfe9fd9033c607ada35ef5b Mon Sep 17 00:00:00 2001 From: George FunBook Date: Mon, 24 Jun 2024 14:41:08 -0500 Subject: [PATCH 15/15] doc --- flixel/text/FlxBitmapText.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index d4235e3ccb..bc9ac97171 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -1739,8 +1739,13 @@ enum WordSplitConditions WIDTH(minPixels:Int); } -typedef TileDrawItemRaw = { final charCodeRaw:Float; final x:Float; final y:Float; }; +@:noCompletion +private typedef TileDrawItemRaw = { final charCodeRaw:Float; final x:Float; final y:Float; }; @:forward +/** + * Internal type used for batch rendering characters, used by FlxBuffer + * **Warning:** all methods must be inlined + */ abstract TileDrawItem(TileDrawItemRaw) from TileDrawItemRaw { public var charCode(get, never):Int;