diff --git a/color.d b/color.d index 74558d7..2d3d270 100644 --- a/color.d +++ b/color.d @@ -1577,6 +1577,8 @@ struct Rectangle { Implements a flood fill algorithm, like the bucket tool in MS Paint. + Note it assumes `what.length == width*height`. + Params: what = the canvas to work with, arranged as top to bottom, left to right elements width = the width of the canvas @@ -1585,13 +1587,17 @@ struct Rectangle { replacement = the replacement value x = the x-coordinate to start the fill (think of where the user clicked in Paint) y = the y-coordinate to start the fill - additionalCheck = A custom additional check to perform on each square before continuing. Returning true means keep flooding, returning false means stop. + additionalCheck = A custom additional check to perform on each square before continuing. Returning true means keep flooding, returning false means stop. If null, it is not used. +/ void floodFill(T)( T[] what, int width, int height, // the canvas to inspect T target, T replacement, // fill params int x, int y, bool delegate(int x, int y) @safe additionalCheck) // the node + + // in(what.length == width * height) // gdc doesn't support this syntax yet so not gonna use it until that comes out. { + assert(what.length == width * height); // will use the contract above when gdc supports it + T node = what[y * width + x]; if(target == replacement) return; @@ -1604,6 +1610,44 @@ void floodFill(T)( if(!additionalCheck(x, y)) return; + Point[] queue; + + queue ~= Point(x, y); + + while(queue.length) { + auto n = queue[0]; + queue = queue[1 .. $]; + //queue.assumeSafeAppend(); // lol @safe breakage + + auto w = n; + int offset = cast(int) (n.y * width + n.x); + auto e = n; + auto eoffset = offset; + w.x--; + offset--; + while(w.x >= 0 && what[offset] == target && additionalCheck(w.x, w.y)) { + w.x--; + offset--; + } + while(e.x < width && what[eoffset] == target && additionalCheck(e.x, e.y)) { + e.x++; + eoffset++; + } + + // to make it inclusive again + w.x++; + offset++; + foreach(o ; offset .. eoffset) { + what[o] = replacement; + if(w.y && what[o - width] == target && additionalCheck(w.x, w.y)) + queue ~= Point(w.x, w.y - 1); + if(w.y + 1 < height && what[o + width] == target && additionalCheck(w.x, w.y)) + queue ~= Point(w.x, w.y + 1); + w.x++; + } + } + + /+ what[y * width + x] = replacement; if(x) @@ -1621,6 +1665,7 @@ void floodFill(T)( if(y != height - 1) floodFill(what, width, height, target, replacement, x, y + 1, additionalCheck); + +/ } // for scripting, so you can tag it without strictly needing to import arsd.jsvar diff --git a/gamehelpers.d b/gamehelpers.d index f041d07..0295259 100644 --- a/gamehelpers.d +++ b/gamehelpers.d @@ -394,6 +394,7 @@ final class OpenGlTexture { /// After you delete it with dispose, you may rebind it to something else with this. void bindFrom(TrueColorImage from) { + assert(from !is null); assert(from.width > 0 && from.height > 0); import core.stdc.stdlib; diff --git a/jsvar.d b/jsvar.d index 7a027a2..8abef87 100644 --- a/jsvar.d +++ b/jsvar.d @@ -549,6 +549,7 @@ struct var { } } + /// `if(some_var)` will call this and give behavior based on the dynamic type. Shouldn't be too surprising. public bool opCast(T:bool)() { final switch(this._type) { case Type.Object: @@ -568,6 +569,7 @@ struct var { } } + /// You can foreach over a var. public int opApply(scope int delegate(ref var) dg) { foreach(i, item; this) if(auto result = dg(item)) @@ -575,6 +577,7 @@ struct var { return 0; } + /// ditto public int opApply(scope int delegate(var, ref var) dg) { if(this.payloadType() == Type.Array) { foreach(i, ref v; this._payload._array) @@ -606,15 +609,30 @@ struct var { } + /// Alias for [get]. e.g. `string s = cast(string) v;` public T opCast(T)() { return this.get!T; } + /// Calls [get] for a type automatically. `int a; var b; b.putInto(a);` will auto-convert to `int`. public auto ref putInto(T)(ref T t) { return t = this.get!T; } - // if it is var, we'll just blit it over + /++ + Assigns a value to the var. It will do necessary implicit conversions + and wrapping. + + You can make a method `toArsdJsvar` on your own objects to override this + default. It should return a [var]. + + History: + On April 20, 2020, I changed the default mode for class assignment + to [wrapNativeObject]. Previously it was [wrapOpaquely]. + + With the new [wrapNativeObject] behavior, you can mark methods + @[scriptable] to expose them to the script. + +/ public var opAssign(T)(T t) if(!is(T == var)) { static if(__traits(compiles, this = t.toArsdJsvar())) { static if(__traits(compiles, t is null)) { @@ -661,7 +679,10 @@ struct var { // so prewrapped stuff can be easily passed. this._type = Type.Object; this._payload._object = t; - } else static if(is(T == class) || .isScriptableOpaque!T) { + } else static if(is(T == class)) { + this._type = Type.Object; + this._payload._object = wrapNativeObject(t); + } else static if(.isScriptableOpaque!T) { // auto-wrap other classes with reference semantics this._type = Type.Object; this._payload._object = wrapOpaquely(t); @@ -846,6 +867,31 @@ struct var { return null; } + /++ + Gets the var converted to type `T` as best it can. `T` may be constructed + from `T.fromJsVar`, or through type conversions (coercing as needed). If + `T` happens to be a struct, it will automatically introspect to convert + the var object member-by-member. + + History: + On April 21, 2020, I changed the behavior of + + --- + var a = null; + string b = a.get!string; + --- + + Previously, `b == "null"`, which would print the word + when writeln'd. Now, `b is null`, which prints the empty string, + which is a bit less user-friendly, but more consistent with + converting to/from D strings in general. + + If you are printing, you can check `a.get!string is null` and print + null at that point if you like. + + I also wrote the first draft of this documentation at that time, + even though the function has been public since the beginning. + +/ public T get(T)() if(!is(T == void)) { static if(is(T == var)) { return this; @@ -920,7 +966,7 @@ struct var { } else static if(isSomeString!T) { if(this._object !is null) return this._object.toString(); - return "null"; + return null;// "null"; } else return T.init; case Type.Integral: @@ -1126,18 +1172,22 @@ struct var { return var(null); } + /// Forwards to [opIndex] public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__)() { return this[name]; } + /// Forwards to [opIndexAssign] public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__, T)(T r) { return this.opIndexAssign!T(r, name); } + /// Looks up a sub-property of the object public ref var opIndex(var name, string file = __FILE__, size_t line = __LINE__) { return opIndex(name.get!string, file, line); } + /// Sets a sub-property of the object public ref var opIndexAssign(T)(T t, var name, string file = __FILE__, size_t line = __LINE__) { return opIndexAssign(t, name.get!string, file, line); } diff --git a/minigui.d b/minigui.d index e977fd7..8846348 100644 --- a/minigui.d +++ b/minigui.d @@ -3150,7 +3150,7 @@ class TabWidget : Widget { } override int marginTop() { return 4; } - override int marginBottom() { return 4; } + override int paddingBottom() { return 4; } override int minHeight() { int max = 0; diff --git a/simpledisplay.d b/simpledisplay.d index 1630fde..1c9584e 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -13459,6 +13459,8 @@ extern(System) nothrow @nogc { uint/*GLenum*/ format, uint/*GLenum*/ type, in void* pixels); void glTexEnvf(uint/*GLenum*/ target, uint/*GLenum*/ pname, float param); + void glLineWidth(int); + void glTexCoord2f(float, float); void glVertex2i(int, int); diff --git a/ttf.d b/ttf.d index 4345b93..cf0b4cc 100644 --- a/ttf.d +++ b/ttf.d @@ -16,6 +16,8 @@ module arsd.ttf; // here's some D convenience functions +// need to do some print glyphs t it.... + /// struct TtfFont {