BplusCore.Utilities.BinaryType

A decorator for numbers that outputs their bit pattern for both print() and show(). E.x. "0b00111011".

The number type TNum must support the following:

* `sizeof() * 8` to determine the max number of bits.
    * If your type doesn't do this, overload `binary_size(n::TNum)`
        or `binary_size(::Type{TNum})`.
* To check individual bits: bitwise `&` and `<<`;
    `zero(TNum)` that makes a value with all bits set to `0`;
    `one(TNum)` that makes a value with all bits set to `0` *except* the lowest bit.
    * If your type doesn't do this, overload `is_bit_one(a::TNum, bit_idx::Int)::Bool`
BplusCore.Utilities.ConstPRNGType

An immutable version of PRNG, to avoid heap allocations. Any calls to rand() with this PRNG will return a tuple of 1) the result, and 2) the new 'mutated' ConstPRNG.

This is a break from the typical AbstractRNG interface, so it only supports a specific set of rand() calls.

You can construct a PRNG with its fields (UInt32 and NTuple{3, UInt32}). You can also construct it with any number of scalar data parameters, to be hashed into seeds. If no seeds are given, one is generated with rand(UInt32).

When constructing it, you can optionally pass a 'mixing' strength as the first argument, wrapped in the compile-time type Val. Weaker strengths result in better performance, but risk creating artifacts. The default is Val(PrngStrength.strong), and with more seed values you can get away with weaker ones.

BplusCore.Utilities.ConstVectorType

An immutable alternative to Vector, using tuples. The size is a type parameter, but you can omit it so that it's 'resizable'.

BplusCore.Utilities.EnumeratedPairingType

Wraps an iterator so that it can be unwrapped by calling pairs(). Each element's key in the pair is an index, using enumerate().

This is needed to use iterators/generators in functions like findfirst().

BplusCore.Utilities.FunctionMetadataType

Returns nothing if the expression doesn't look like a valid function definition (i.e. it's wrapped by an unexpected macro, or not even a function in the first place).

Pass false for check_function_grammar to ignore the actual function inside the macros.

BplusCore.Utilities.FunctionMetadataType

Information that can be part of a function definition but isn't handled by MacroTools. For example, @inline, @generated, and doc-strings.

Turn this back into code with MacroTools.combinedef(::FunctionMetadata).

BplusCore.Utilities.InteropStringType

Combines a Julia UTF-8 string with a null-terminated C-string buffer.

Use update!() to set the Julia string or recompute it from the C buffer.

BplusCore.Utilities.IterSomeType

An iterator that runs your lambda, passing it an index, until you return nothing instead of Some{T}. Each element you return is then an element of this iterator.

If you know the type of elements being emitted, you can specify it to help type-inference.

BplusCore.Utilities.PRNGType

A fast, strong PRNG, taken from http://burtleburtle.net/bob/rand/smallprng.html. Normally outputs 32-bit numbers; other sizes require extra ops.

BplusCore.Utilities.SerializedUnionType

A Union of types that can be serialized/deserialized through StructTypes. Any types that can individually be handled by StructTypes should also be usable in this union. It is not super efficient, as it uses try/catch to find the first type that successfully deserializes.

BplusCore.Utilities.SplitArgType

A data represenation of an argument declaration (a.k.a. the output of MacroTools.splitarg()). Also handles the whole argument declaration being wrapped in an esc(), which splitarg() does not.

BplusCore.Utilities.SplitDefType

A data representation of the output of MacroTools.splitdef(), plus the ability to recognize meta-data like doc-strings and @inline.

For convenience, it can also represent function signatures (i.e. calls), by setting the body to nothing (not to be confused with :nothing).

It can also represent lambdas by setting the name to nothing.

It is not valid to set both the body and the name to nothing.

BplusCore.Utilities.SplitMacroType

A data representation of a macro invocation, analogous to SplitDef and SplitArg. The constructor returns nothing if the expression isn't a macro invocation.

Turn this struct back into a macro call with combinemacro().

BplusCore.Utilities.SplitTypeType

A data representation of a type declaration, such as C{R, T<:Integer} <: B. Analogous to SplitDef and SplitArg.

The constructor returns nothing if the expression isn't a macro invocation.

If you want to skip type checking (such as the name being a Symbol), pass false in the constructor.

Base.append!Method

Provides append!() for sets, which is missing from Julia for some reason

BplusCore.Utilities.compute_opMethod

Computes one of the modifying assignments (*=, &=, etc) given it and its inputs. Also implements = for completeness.

BplusCore.Utilities.enumerate_as_pairMethod

Wraps an iterator so that it can be unwrapped by calling pairs(). Each element's key in the pair is an index, using enumerate(). This is needed to use iterators/generators in functions like findfirst().

BplusCore.Utilities.generate_enumMethod

The inner logic of @bp_enum. Also takes a block of "definitions", in case something needs to be imported into the enum's module.

BplusCore.Utilities.is_scopable_nameFunction

Checks that an expression is a Symbol, possibly nested within . operators. For example, Base.empty.

Optionally allows type parameters at the end of the expression, like :( A.B{C} ).

BplusCore.Utilities.is_short_function_declMethod

Checks if an expression is a short-form function declaration (like f() = 5). Note that MacroTools' version of this function accepts things that are not actually functions.

If check_components is true, then the checks become a little stricter, such as checking that the function name is a valid expression.

BplusCore.Utilities.iterMethod

A facade for using unordered collections in functions that don't normally accept them (like map)

BplusCore.Utilities.rand_ntupleMethod

Picks a random element from an ntuple. Unfortunately, Random.rand(::ConstPRNG, ::NTuple) has unavoidable type ambiguity.

BplusCore.Utilities.reinterpret_bytesMethod

Converts between two data representations by reinterpreting the bytes. For example:

  • Get the individual bytes of a uint with (a, b, c, d) = reinterpret_bytes(0xaabbccdd, NTuple{4, UInt8})
  • Copy a struct into a Vector{UInt8} with reinterpret_bytes(my_struct, my_vector).
  • Read the first 4 bytes of an array-view as a Float32: f = reinterpret_bytes(@view(my_byte_array[i*4 : end]), Float32)
BplusCore.Utilities.union_orderingMethod

When deserializing a union of types, this determines the order that the types are tried in. Lower-priority values are tried first.

BplusCore.Utilities.unzipMethod

Takes a zipped piece of data and unzips into the original iterators.

Anything that behaves like the output of zip() can be similarly unzipped; however this general implementation will iterate on zipped several times, and may not even be type-stable (we have to test it).

If the number of zipped iterators can't be deduced statically, you should feed it in as the second argument.

BplusCore.Utilities.visit_exprsMethod

Walks through an expression depth-first, invoking your lambda on every sub-expression with the following arguments:

1 .A list of integers representing the path to this sub-expression (for each Expr along the way, the index of its argument)

  1. A list of the parents to this sub-expression, from the root down to the current expr.

For example, you could pass a lambda of the form (path, exprs) -> let e = exprs[end] ... end.

BplusCore.Utilities.@ano_enumMacro

Anonymous enums, implemented with Val. A stricter alternative to passing symbols as parameters. @ano_enum(ABC, DEF, GHI) == Union{Val{:ABC}, Val{:DEF}, Val{GHI}} Note that this creates some overhead if the user can't construct the Val at compile-time.

BplusCore.Utilities.@bp_bitflagMacro

Combines the features of @bp_enum with the bitfield behavior of @bitflag from BitFlags.jl. Along with the usual @bp_enum definitions, you also get:

  • Power-of-two element numbering by default (if you want a 0 element, put it at the beginning).
  • Aggregate elements with the syntax @element_name a|b|c. Note that these are not enumerated in instances().
  • a | b to combine two bitflags.
  • a & b to filter bitflags.
  • a - b to remove bitflags (equivalent to a & (~b)).
  • Subsets: a <= b means "a is a subset of b", and >= means vice-versa.
  • Base.contains(haystack, needle)::Bool as a short-hand for (haystack & needle) == needle
  • ALL as a value representing all elements of the bitfield enabled
  • Pretty printing of combination values.
BplusCore.Utilities.@bp_enumMacro

An alternative to the @enum macro, with the following differences:

  • Keeps the enum values in a module to prevent name collisions.
  • Provides an overload of base.parse() to parse the enum from a string.
  • Provides MyEnum.from(i::Integer) to convert from int to enum.
  • Provides MyEnum.from_index(i::Integer) to get an enum value from its index in the original declaration.
  • Provides MyEnum.to_index(e) to get the index of an enum value in the original declaration.
  • Provides MyEnum.instances() to get a tuple of the elements.
  • Provides an alias for the enum type, E_MyEnum.

If you wish to add definitions inside the enum module (e.x. import a package), make your own custom macro that returns generate_enum(). put a begin ... end block just after the enum name, containing the desired code.

If you want your enum to act like a bitfield, use @bp_bitflag instead.

BplusCore.Utilities.@decentralized_module_initMacro

Implements a module's init function by delegating it to a list of callbacks. This allows multiple independent places in your module to register some initialization behavior. The callback list will be named RUN_ON_INIT.

BplusCore.Utilities.@f32Macro

Game math is mostly done with 32-bit floats, especially when interacting with computer graphics. This is a quick short-hand for making a 32-bit float.

BplusCore.Utilities.@make_toggleable_assertsMacro

This is an alternative to ToggleableAsserts.jl. That package allows you to call asserts which can be toggled on or off by the JIT compiler; however all projects using the package must respect a single global flag.

This macro generates ToggleableAsserts-style code, so that you can have multiple separate instances of ToggleableAsserts. B+ uses this to give each sub-module its own debug flag, and you can use it to give your project its own debug flag as well.

Invoking @make_toggleable_asserts X_ generates the following:

  • @X_assert(condition, msg...) performs a check if X_asserts_enabled(), and throws an error if the check fails.
  • @X_debug() evaluates to a boolean for whether or not asserts are enabled.
  • @X_debug a [b], to evaluate 'a' if asserts are enabled, and optionally 'b' otherwise.
  • X_asserts_enabled() represents the debug flag; it's a function that returns the constant false.
    • For debug mode, your scripts can redefine it to return true instead.

These generated items are not exported by default. Additionally, it's configured for "release" mode by default so that release builds don't pay the extra JIT cost. When running tests, redefine X_asserts_enabled() = true.

Note that constants are not part of JIT recompilation, so a const global should never be defined in terms of the above macros.

BplusCore.Utilities.@optionalMacro

A value (or values) that may or may not exist, based on a condition. Useful for conditionally passing something into a collection or function. E.x. print(a, " : ", b, @optional(i>0, " i=", i))

BplusCore.Utilities.@optionalkwMacro

A keyword parameter to a function that may or may not exist. Example usage:

my_func(; @optionalkw(a>10, my_func_arg, a))
BplusCore.Utilities.@shoutMacro

Prints the current file and line, along with any data you pass in. Helps pin down crashes that don't leave a clear stack trace.

BplusCore.Utilities.@unionspecMacro

Union of some outer type, specialized for many inner types. Examples:

  • @unionspec(Vector{_}, Int, Float64) == Union{Vector{Int}, Vector{Float64}}
  • @unionspec(Array{Bool, _}, 2, 4) == Union{Array{Bool, 2}, Array{Bool, 4}}
BplusCore.Math.ContiguousType

Any kind of contiguous data, suitable for passing into a C function as a raw pointer. Includes everything from a single bare element, to something like Vector{NTuple{4, v3f}}.

It's not actually possible to explicitly list the infinite variety of possible nestings, so this union currently supports up to 3 levels, which should be enough.

BplusCore.Math.ContiguousRawType

Collections which can be contiguous in memory, even when nested arbitrarily-many times. Includes the type itself, i.e. Int <: ContiguousRaw{Int}.

The length of these containers can be determined with only the type information.

BplusCore.Math.ContiguousSimpleType

A flat, contiguous collection of some type T. Includes the type itself, i.e. Int <: ContiguousSimple{Int}. A Ref{T} is assumed to have length 1.

BplusCore.Math.BoxType

An axis-aligned bounding box of some number of dimensions.

Can be deserialized through StructTypes using almost any pair of properties – 'min' + 'size'; 'min' + 'max'; 'max' + 'size'; 'center' + 'size', etc.

BplusCore.Math.BoxMethod

Constructs a Box covering the given range. Ignores the step size.

BplusCore.Math.ContiguousRefType

Ref to a contiguous set of data, possibly nested (e.x. Float32s in a Vector{NTuple{4, v3f}}). Note that the index is in terms of the individual contiguous elements.

BplusCore.Math.MatType

The type for a matrix with the given rows, columns, and component type. The last parameter must be the number of elements (a.k.a. R*C).

BplusCore.Math.QuaternionType

Quaternions are great for representing 3D rotations. It is assumed to always be normalized, to optimize the math when reversing Quaternions and rotating vectors.

BplusCore.Math.TrueOrderingType

Indexes an array using the vector's reversed elements, so that for example the y component of a 2D index points to a row rather than a column. This is more intuitive when working with texture pixel arrays.

Also see the true_order argument for vsize().

BplusCore.Math.VecType

A vector math struct. You can get its data from:

  • The field "data", e.x. v.data::NTuple
  • Individual XYZW or RGBA components, e.x. v1.x == v1.r
  • Swizzling, e.x. v.yyyz == Vec(v.y, v.y, v.y, v.z).

Swizzles can also use 0 for a constant 0, 1 for a constant 1, (\increment) for the max finite value, and (\nabla) for the min finite value. For example, Vec{UInt8}(128, 4, 200).rgb∆ results in Vec{UInt8}(128, 4, 200, 255).

Vec has an efficient implementation of AbstractVector, including map, foldl, etc, except for broadcasting because I don't know how to do it yet.

You can use the colon operator to iterate between two values (e.x. Vec(1, 1) : Vec(10, 10)).

To change how many digits are printed in the REPL and Base.show(), set VEC_N_DIGITS or call use_vec_digits() do ... end.

NOTE: Comparing two vectors with == or != returns a boolean as expected, but other comparisons (<, <=, >, >=) return a component-wise result.

Base.:<<Method

Chain Quaternion rotations in the order signified by the arrow. E.x. a << b means "Rotate by b, then rotate by a".

Base.:>>Method

Chain Quaternion rotations in the order signified by the arrow. E.x. a >> b means "Rotate by a, then rotate by b".

Base.containsMethod

Gets whether the first box contains the second

Base.findminMethod

Finds the minimum component which passes a given predicate

Base.intersectMethod

Calculate the intersection of two or more boxes. If they don't intersect, it will have 0 or negative size (see is_empty()).

Base.reshapeMethod

Changes the dimensionality of the box. By default, new dimensions are given the size 1 (both for integer and float boxes).

BplusCore.Math.contiguous_lengthMethod

Gets the total length of a contiguous array of T data, regardless of how deeply it's nested.

Note that there's an implementation limit on how deeply-nested the array can really be; see Contiguous{T} if you get MethodErrors.

BplusCore.Math.contiguous_ptrMethod

Gets a Ptr{T} to an element of a contiguous array of T data, regardless of how deeply it's nested. Takes an index into the element (defaulting to 1), but be aware that this is on top of the Ref itself! If the Ref wasn't generated with contiguous_ref(), then it may already be offsetting from the first element.

Note that there's an implementation limit on how deeply-nested the array can really be; see Contiguous{T} if you get MethodErrors.

BplusCore.Math.fractMethod

Gets the fractional part of f.

Negative values wrap around; for example fract(-0.1) == 0.9 (within floating-point error). This matches the behavior of GLSL's fract(), and I believe HLSL's frac() too.

BplusCore.Math.get_right_handedMethod

Gets whether coordinate math is right-handed. By default, is true.

Redefine this (get_right_handed() = false) to make your project left-handed. Julia's JIT will allow it to act as a compile-time constant after the initial overhead of recompilation.

For example, you might want to do this if your game is primarily rendered through Dear ImGUI.

BplusCore.Math.get_up_axisMethod

Gets the vertical axis – 1=X, 2=Y, 3=Z. By default, uses Z as the up-axis.

Redefine this (e.x. get_up_axis() = 2) to change the vertical axis for your project. Julia's JIT will allow it to act as a compile-time constant after the initial overhead of recompilation.

BplusCore.Math.get_up_signMethod

Gets the sign of the "Upward" direction of the vertical axis. For example, if get_up_axis()==3 && get_up_sign()==-1, then the "Up" direction is pointing in the -Z direction.

By default, points in the positive direction (i.e. returns -1).

Like get_up_axis(), you can redefine this to configure coordinate math for your project.

BplusCore.Math.intersectionsMethod

Finds the intersection(s) of a ray passing through a shape. Intersections are represented as distances along the ray. They are returned in ascending order (first intersection is the closest one).

Optional parameters:

  • min_t: the beginning of the ray. Defaults to 0.
  • max_t: the end of the ray. Defaults to the largest finite value.
  • atol: margin of error for various calculations.

For 3D shapes, you may pass an optional type param to add the first intersection's surface normal to the return value. The normal is undefined if there are no intersections.

BplusCore.Math.inv_lerpMethod

The inverse of lerp(). Given a min, max, and value, finds the interpolant for that value.

BplusCore.Math.is_insideMethod

Gets whether the point is fully inside the box, not touching the edges. This is primarily for integer boxes.

BplusCore.Math.lerpMethod

Linear interpolation: a smooth transition from a to b based on a t. t=0 results in a, t=1.0 results in b, t=0.5 results in the midpoint between them.

You may also pass a bool (or Vec of bools) for t.

BplusCore.Math.lerpMethod

Interpolation between two quaternions. It's simple (and fast) linear interpolation, which can lead to strange animations when tweening. Use q_slerp() instead for smoother rotations.

BplusCore.Math.m4_worldMethod

Builds the world-space matrix for an object with the given position, rotation, and scale.

BplusCore.Math.m_apply_point_affineMethod

Applies a transform matrix to the given coordinate, assuming the homogeneous component stays at 1.0 and does not need to be processed. This is generally true for world and view matrices, but not projection matrices.

BplusCore.Math.m_apply_vector_affineMethod

Applies a transform matrix to the given vector (i.e. ignoring translation), assuming the homogeneous component stays at 1.0 and does not need to be processed. This is generally true for world and view matrices, but not projection matrices.

BplusCore.Math.perlinMethod

Highly customizable, N-dimensional Perlin noise. Output range is 0 - 1.

The seeds tuple provides extra seed data to the perlin gradient calculation.

prng_strength is a compile-time flag for the strength of the PRNG. Stronger ones require more warm-up time.

filter_pos can be used to implement wrapping or clamping.

t_curve can be used to change the smoothness of the noise by taking a linear t value and converting it to a smoother one. Best results come from a function with first- and second-derivatives of 0 at t=0 and t=1.

BplusCore.Math.q_slerpMethod

Interpolation between two rotations. The interpolation is nonlinear, following the surface of the unit sphere instead of a straight line from rotation 1 to rotation 2, but is a bit expensive to compute. The quaternions' 4 components must be normalized.

BplusCore.Math.qnormMethod

Normalizes a quaternion. You normally don't need to do this, but floating-point error can accumulate over time, causing quaternions to "drift" away from length 1.

BplusCore.Math.rand_in_sphereMethod

Picks a random position within a unit radius sphere centered at the origin.

Requires 3 uniform-random values.

BplusCore.Math.round_up_to_multipleMethod

Rounds a value up to the nearest multiple of another value. Works with both integers and Vecs of integers (Vec{<:Integer}).

Behavior with float inputs is not defined, because I haven't bothered to think about it (feel free to do it yourself and update this comment!)

Behavior with negative inputs is not defined, because I haven't bothered to think about it (feel free to do it yourself and update this comment!)

BplusCore.Math.smootherstepMethod

An even smoother version of smoothstep(), at the cost of a little performance. Again, unlike lerp(), this function clamps t to the range (0, 1), as its behavior outside this range is not intuitive.

BplusCore.Math.smoothstepMethod

Smooth interpolation: a non-linear version of lerp that moves faster around 0.5 and slower around the ends (0 and 1), creating a more organic transition. Unlike lerp(), this function clamps t to the range (0, 1), as its behavior outside this range is not intuitive.

BplusCore.Math.v_rightwardMethod

Does a cross product in the correct way to get a rightward vector from forward and upward ones

BplusCore.Math.vbasisMethod

Constructs an orthogonal 3D vector basis as similar as possible to the input vectors. If the vectors are equal, then the up axis will usually be calculated with the global up axis. Returns the new forward and up vectors.

BplusCore.Math.vindexMethod

Converts a multidimensional array index to a flat one, assuming X is the innermost component

BplusCore.Math.vindexMethod

Converts a flat array index to a multidimensional one, assuming X is the innermost component

BplusCore.Math.vselectMethod

Like a binary version of lerp, or like step(). Returns components of a when t is false, or b when t is true.

BplusCore.Math.vsizeFunction

Gets the size of an array as a Vec, rather than an NTuple.

If you pass true_order=true, then the elements of the vector will be reversed, so that for example y measures the number of rows in a 2D array rather than columns. Also see TrueOrdering.