@page ax AX
@section axlanguage AX Language Documentation
@par
Welcome to the AX Language documentation. These docs provide detailed
information on the AX language including syntax, data types, available
functionality (including OpenVDB specific methods) execution structure and
control flow. See the @subpage axcplusplus documentation for the C++ developer
documentation.
@note
Some sections of this document are still to be completed. Please get in touch
should you have any questions!
@section axcontents Contents
- @ref axintro
- @ref axprograms
- @ref axprogramexample
- @ref axexecutionxcontext
- @ref axdatatypes
- @ref axscalars
- @ref axvecmats
- @ref axstrings
- @ref axtypeprecedence
- @ref axoperators
- @ref axopbinary
- @ref axopassignment
- @ref axopbinarithmetic
- @ref axopcomparison
- @ref axopbinlogical
- @ref axopunary
- @ref axopunarithmetic
- @ref axopunlogical
- @ref axopunincdec
- @ref axopaccess
- @ref axopother
- @ref axopprecedence
- @ref axtokens
- @ref axvaridentifiers
- @ref axliterals
- @ref axcomments
- @ref axkeywords
- @ref axreserved
- @ref axsyntax
- @ref axattribaccess
- @ref axexternalaccess
- @ref axdecls
- @ref axscopes
- @ref axbranching
- @ref axloops
- @ref axfunctions
- @ref axuserfunctions
- @ref axvexsupport
- @ref axexamples
@section axintro Introduction
@par
AX is a fast and portable JIT compiled expression language, designed for
writing volume and point kernels represented as OpenVDB grids. It was originally
developed by the RNDFX team at
DNEG to
provide a more consistent way for artists to be able to interface with OpenVDB
data across a variety of in house and external digital content creation software,
whilst retaining the performance achievable by custom C++ operators.
@par
The language is predominately inspired by
SideFX's VEX
language which itself takes elements from C, C++ and the Renderman Shading
Language. The design of AX uses concepts from these languages and refrains
from deviating from their syntax too significantly. Specifically, it does not
aim to change what are already well established and heavily used language
syntaxes within the Visual Effects industry; on the contrary, AX aims to provide
an interface to which a user with previous or little programming experience can
easily transition to. However, it does introduce new concepts where the
creators deemed necessary to provide the best and most representative syntax
relating to the design for OpenVDB volume and point modification.
@section axprograms AX Programs
@par
AX programs represent a set of statements which read and write to geometry,
with each program designed in such a way that it can be executed in a highly
parallelized framework to access and update individual geometric components.
For example, consider a mesh with point, vertex and primitive
attributes. AX programs are designed to run independently across "elements" of
input geometry and process "attributes" from a particular element. In this case,
the element level would correlate to either points, vertexes or primitives, and
a single element would be a unique instance of one of these. These programs are
at their most efficient writing to the currently processing element, however do
provide varying levels of access to the geometry as a whole.
@par
Note that native AX only supports the modification of OpenVDB data. Currently,
that means that native AX programs can be written and built for execution over
OpenVDB Points or OpenVDB Volumes. For any given AX program, the execution context
(what it's processing) may change what functionality is available and, more
importantly, the behaviour of reading/writing from attributes. See the @ref axexecutionxcontext
for details.
@par
Whilst powerful, AX is not a catch all replacement for all types of VDB
operations. Some of this is related to its infancy i.e. missing native support
for some useful functions, the foundations of which may be supported by AX but
have not yet been exposed. However there may be certain paradigms which AX is
better tailored to support than others. Grid reductions, for example, typically
require the passing of a state between programs; a design pattern which AX is
less equipped to handle. AX kernels can be abstractly thought of as a "foreach()"
function (see @ref axprogramexample) and whilst there are ways to achieve
reductions, it is an example which would be better suited to a custom C++
implementation.
@par
For details of extending AX for custom geometry, see the @subpage axcplusplus
developer documentation. Note that custom geometry support requires C++ extension.
@subsection axprogramexample A Program Example
@par
Before getting into the technical depths of the language, it's a good idea to
observe a small but complete AX program which could be compiled and executed.
To begin with, here is a very simple example of a program that reads and writes
to a particular attribute:
@par
@code{.c}
// read a floating point attribute from the value of "myattribute"
float temp = float@myattribute;
// if the value is less than 0, clamp it to 0
if (temp < 0.0f) float@myattribute = 0.0f;
@endcode
Note that there is no other required logic here to make this a compatible
program i.e. feeding this code to the @ref vdbaxbinary "vdb_ax command line binary"
will compile and execute over provided OpenVDB files.
@par
The following explains the above example in relation to OpenVDB data; OpenVDB
points and OpenVDB volumes. The example demonstrates reading from a value on
some input, either a point attribute or a voxel value, and storing the
result in a @ref axdecls "local variable". Importantly, the attribute being
accessed has the name @b `myattribute` and the type @b `float`. The first statement:
@code{.c}
float temp = float@myattribute;
@endcode
Invokes a @b read from this attribute (see @ref axattribaccess), retrieving the
value from the geometry and storing it in the variable @b `temp` to be used in
the AX program. The second statement:
@code{.c}
if (temp > 0.0f) float@myattribute = 0.0f;
@endcode
Performs a comparison of this value with @b `0.0f` and, if the value is greater
than @b `0.0f`, performs a @b write to the geometry in the form of
`float@myattribute = 0.0f;` which sets the value of this attribute on the
geometry to @b `0.0f`.
@par
For OpenVDB Points, this kernel is run over every point in an OpenVDB Points
Grid. In simple pseudo code:
@code{.unparsed}
foreach(point in pointgrid)
run_ax_kernel(point)
done()
@endcode
Where @b `pointgrid` is a single Point Data Grid. For OpenVDB volumes the kernel
is run over each voxel in a provided grid:
@code{.unparsed}
foreach(grid in grids)
foreach(voxel IN grid)
run_ax_kernel(voxel)
done()
done()
@endcode
@par
Where @b `grids` can comprise of any number of OpenVDB volumes. In this program,
only a floating point grid with the name @b `myattribute` will be updated. Each
voxel in this grid will have it's value compared in the same way as the points
example given above.
@subsection axexecutionxcontext OpenVDB Execution Context
@par
AX programs are designed to run over different types of grids, specifically
OpenVDB points and OpenVDB volumes, the latter of which is the set of all default
supported mathematic volume types in OpenVDB. Programs must be compiled separately
for points and volumes, or twice to support both (see the @ref vdbaxbinary "vdb_ax binary").
This is known as compiling for a target
Execution Context. There are two
main considerations to make when switching between execution contexts:
- Certain @ref axfunctions "functions" may not be available under a given
context. Some natively supported functions are designed to interface
directly with geometry attributes, and a further subset of these are tailored
specifically for point attributes @b or voxel values, not both.
- Whilst the AX grammar does not change syntactically, @ref axattribaccess
"attribute accesses" may be affected. This is due to the fundamental
differences of executing over different types of VDBs/geometry.
@par Execution over Points
AX kernels compiled for OpenVDB Points run @b individually on
each point
in every
active voxel in a points VDB. Attributes that have been
accessed in the program are provided such that the program has access to all
data available on the currently processing point. Multiple OpenVDB Point grids
can be processed by the same AX program, but only a single OpenVDB Points grid
can be processed at a time. The default behaviour for AX point programs is to
process every point which exists in active OpenVDB voxels.
@par Execution over Volumes
AX kernels for OpenVDB volumes run individually on every
active voxel of
VDBs which are @b written to (whilst retaining access to all available VDBs).
Volumes can be thought of as only holding a single "attribute". Whilst points
hold all attributes within a single VDB points grid (and values for all attributes
are are defined for all points in that grid), multiple volume attributes require
multiple VDB volumes to be accessed. The location of this access is the
world space position of each voxel. Assignment operations determine which
volumes will be executed over.
@par
See the syntax section on @ref axattribaccess "accessing attribute" for more
information.
@section axdatatypes Data Types
@par
AX supports a rich variety of native types to be able to maximise the efficiency
of arithmetic operations and facilitate accessing underlying geometry. As AX is
designed for OpenVDB, native AX types aim to provide direct access to
all supported OpenVDB data types. You may find, however, that some AX types do
not exist as OpenVDB volume/point types or refuse to be serialized in some
installations of OpenVDB. The table below lists all available types in AX, the
exposed @ref axattribaccess "attribute access" syntax and whether or not the
attribute syntax is valid for both point and volume execution contexts.
Developers installing OpenVDB and OpenVDB AX may wish to read the
@ref vdbaxtoaxtypes "type registry documentation" which explains how to enable
missing types from OpenVDB.
@anchor axdatatypestable
@par
Category | Type | Definition | Attribute Syntax | Points | Voxels |
@ref axscalars |
`bool` |
Boolean value, true or false | `bool@` |
@b Yes | @b Yes |
`int16`* | 16-bit signed integer value | `int16@` | @b Yes | No |
`int32` | 32-bit signed integer value | `int32@, int@, i@` | @b Yes | @b Yes |
`int` | Alias for integer type (typically int32) |
`int64` | 64-bit signed integer value | `int64@` | @b Yes | @b Yes |
`float` | 32-bit floating point value | `float@ f@ @` | @b Yes | @b Yes |
`double` | 64-bit floating point value | `double@` | @b Yes | @b Yes |
@ref axvecmats "Vectors" |
`vec2i` |
2-element vector of integer values |
`vec2i@` |
No |
No |
`vec2f` | 2-element vector of float values | `vec2f@` | No | No |
`vec2d` | 2-element vector of double values | `vec2d@` | No | No |
`vec3i` | 3-element vector of integer values | `vec3i@` | @b Yes | @b Yes |
`vec3f` | 3-element vector of float values | `vec3f@ v@` | @b Yes | @b Yes |
`vec3d` | 3-element vector of double values | `vec3d@` | @b Yes | @b Yes |
`vec4i` | 4-element vector of integer values | `vec4i@` | No | No |
`vec4f` | 4-element vector of float values | `vec4f@` | No | No |
`vec4d` | 4-element vector of double values | `vec4d@` | No | No |
@ref axvecmats "Matrices" |
`mat3f` |
3x3-matrix of float values |
`mat3f@` |
@b Yes |
No |
`mat3d` | 3x3-matrix of double values | `mat3d@` | @b Yes | No |
`mat4f` | 4x4-matrix of float values | `mat4f@` | @b Yes | No |
`mat4d` | 4x4-matrix of double values | `mat4d@` | @b Yes | No |
@ref axstrings "Strings" |
`string` |
A string of characters |
`string@` |
@b Yes |
@b Yes |
- @b Note* - There is no support for int16 local variables or integer literals.
@subsection axscalars Scalars
@par
AX Supports boolean, integer and floating point scalar types. @b `int` and
@b `int32` represent 32-bit integer values and @b `int64` represents a 64-bit
value. @b `float` and @b `double` represent 32-bit and 64-bit floating point
values respectively. All scalars, excluding bools, are signed; there are no
other unsigned scalar types in AX.
@par Integer Overflow and Floating Point Truncation
Scalars may be cast from one type to another, either explicitly using the
@ref axopother "cast" operator or implicitly during @ref axopassignment
"assignments" or @ref axopbinarithmetic "binary arthimetic" operations (in which
case the order of @ref axtypeprecedence "type precedence" is observed). Data can
be truncated (converting floats to integrals) or overflow (conversions
from integers of larger bit widths to integers of smaller bit widths) depending
on the source and target types. See the below examples:
@par
This example demonstrates floating point truncation in AX.
@code
float a = 1.1f;
int b = 5.5f; // implict conversion from literal 5.5f. "b" is set to 5
b = a; // implicit conversion from float "a". "b" is set to 1
@endcode
@par
This example demonstrates integer overflow in AX.
@code
int64 a = 2147483648l; // one more than can be held in an int (note the last letter "l")
int b = a; // implicit conversion. "b" is set to -2147483648
@endcode
@par Infinite and NaN values
Floating point values have two additional intrinsic states; @b `inf` and @b `nan`.
These states can occur during invalid floating point arithmetic. AX performs no
checks on arithmetic to catch these values. A typical example of both @b `inf`
and @b `nan` values are division by zero expressions:
@code
float a = 1.0f/0.0f;
print(a); // prints "inf"
@endcode
@code
float a = 0.0f/0.0f;
print(a); // prints some version of "nan"
@endcode
@par
Generally these values are not desired and can cause problems as they propagate
throughout your program.
@subsection axvecmats Vectors / Matrices
@par
All vector and matrix types in AX are implemented as flat arrays of a given size,
with all elements stored contiguously in memory. Both container types are
represented by @ref axdatatypestable "specific tokens" which tell AX functions
and operators how to handle various arithmetic.
@par Vectors
There is currently support for vectors of sizes 2, 3 and 4 elements. The suffix
letter on the vector types denotes the contained elements precision, with the
number corresponding to the vector size. For example, @b `vec2i` denotes a vector
of two 32-bit integer elements. There are no native types for vectors with
@b `bool`, @b `int16` or @b `int64` elements, so these are not supported.
@par Matrices
Matrices are stored in row major layout (lexographical access order) which
matches the representation of a matrix in OpenVDB. Matrix support consists of
@b `float` and @b `double` precision elements with dimensions either @b `3x3` or
@b `4x4` (total size of 9 and 16 elements respectively). There are no integer or
boolean matrix types. Similiar to vectors, the suffix letter on the type denotes
the contained elements precision, with the number corresponding to the matrix
dimension. A matrix of type @b `mat3d` therefor corresponds to a @b `3x3` matrix
(9 elements) with @b `double` precision elements.
@par Element Access
As elements of all matrix and vector types are stored contiguously, they can be
accessed by the @ref axvecaccessop "[] operator". Vector elements can also be
accessed by supported @ref axopaccess ". operator" components and matrix
elements can be accessed with the @ref axmataccessop "[,] operator".
@par Initialization
Both matrices and vector declarations can be flat initialized from scalars or
component initialized from containers of the same size (see @ref axopassignment
"assignments"). However both types can also be represented as temporary containers
without declarations using the @ref axvecmatinit "{,} syntax."
@par A Note on Operators
Most @ref axopbinary "binary operators" only accept vectors of @b equal sizes as
valid left and right operands - however there exists valid @ref axopbinarithmetic
"arithmetic" for combinations of vectors/matrices with scalars and, importantly,
@ref axbinmultop "multiplicative arithmetic" for vectors with matrices, the latter
performing matrix projections (transformations) on vector arguments.
@subsection axstrings Strings
@par
Strings are an array of characters stored in a unique AX type which is
incompatible with @ref axopaccess "container accesses". As such, string support
is currently fairly limited. Characters themselves (users may know this as a
@b `char` type) have no frontend type, so a @b `string` type must be used for
any number of characters (including the empty string). String literals are
represented by enclosing any number of @ref axtokens "supported AX characters"
by quotes
\" \".
@subsection axtypeprecedence Implicit conversion / type precedence
@par
All operators that perform on two or more types must internally run the
operation at a single precision. When mixing different types (e.g. `int +
float`) values may need to be converted to another type before the operation is
performed. To allow for easier writing of expressions, AX will automatically
detect these cases and convert values when necessary. This is known as
implicit type conversion and the way in which the target operation type
is chosen is known as
type precedence.
@par
When referring to type precedence, we are primarily refering to the conversion of
one @b scalar type to another. For vectors, matrices and other containers, this
refers to the conversion of their element type e.g. `mat3f` to `mat3d`. The
conversion rules for more than the element type (e.g. `int` to `mat4f`) are
governed by AX's assignment and operator rules, detailed in the @ref axoperators
section of this documentation.
@par
Type precedence @b only applies to the @b element type of the type in question.
Containers (such as vectors or matrices) may change their element type precision
(e.g. `vec2i` to `vec2f`). Each scalar type has a ranking which is compared, and
the resulting highest rank is chosen as the target type for all values. These
rankings are defined as follows:
- If any element types are of type `double`, all other element types are converted to `double`
- else, if any element types are of type `float`, all other element types are converted to `float`
- else, if any element types are of type `int64`, all other element types are converted to `int64`
- else, if any element types are of type `int32`, all other element types are converted to `int32`
- else, all element types are `bool`
@par
For example:
@par
@code{.c}
int a = 0;
float b = 0.0f;
// In the following, the arithmetic a + b chooses floating point precision as
// defined by the above type precedence rules (i.e. float(a) + b). The temporary
// value created by the arithmetic + will be at float precision. The subsequent
// assignment must then convert the final result back an integer, resulting in
// an expression equal to: a = int(float(a) + b);
a = a + b;
// This example shows arithmetic minus with a float scalar and vec4d. Minus is a
// supported operator of vec4d, however as the element types do not match
// (float vs double), implicit conversion must be observed. With the above rules,
// we can determine the following expression: c = double(b) + c. Note that
// as the result of "double(b) + c" is a vec4d and the target of the subsequent
// assignment ("c") is a vec4d, no further conversion needs to be performed.
vec4d c = 0.0; // double element type
c = b - c;
@endcode
@par
Strings are not included in this precedence as there are no supported arithmetic
binary operations for strings with operand types other than string.
@section axoperators Operators
@par
The below table lists all available operators supported by AX. Note that not all
AX types support all operators and some operators have unique functionality
depending on the operand types.
@par
Operators
|
@ref axopbinary |
@ref axopunary |
Other |
@ref axopassignment |
@ref axopbinarithmetic |
@ref axopcomparison |
@ref axopbinlogical |
@ref axopunarithmetic |
@ref axopunlogical |
@ref axopunincdec |
@ref axopaccess |
@ref axopother |
a = b a += b a -= b a *= b a /= b a %= b
a &= b a |= b a ^= b a <<= b a >>= b
|
a + b a - b a * b a / b a % b a & b a | b
a ^ b a << b a >> b
|
a == b a != b a < b a > b a <= b a >= b
|
a && b a || b
|
+a -a ~a
|
!a
|
++a \-\-a a++ a\-\-
|
a[] a[,] a.b
|
a(...) a, b a ? b : c { a,b ... }
|
@subsection axopbinary Binary Operators
@par
Binary operators are the most common inbuilt operators for AX types. These
operators take two inputs; a left hand side and a right hand side. There exists
a number of valid combinations of AX types with different binary operators, as
well as different implicit casting schemes depending on the binary operation
being performed.
@subsubsection axopassignment Assignments
@par
Assignment operators are the only way to directly modify the value of local and
external data (except for @ref axfunctions which take @b references). The
assignment operators have the following forms:
@par
Operator Name | Operator Syntax | Returns |
Simple assignment | @a `lhs` `=` @a `rhs` | Reference to @a `lhs` |
Addition assignment | @a `lhs` `+=` @a `rhs` | Reference to @a `lhs` |
Subtraction assignment | @a `lhs` `-=` @a `rhs` | Reference to @a `lhs` |
Multiplication assignment | @a `lhs` `*=` @a `rhs` | Reference to @a `lhs` |
Division assignment | @a `lhs` `/=` @a `rhs` | Reference to @a `lhs` |
Modulo assignment | @a `lhs` `%=` @a `rhs` | Reference to @a `lhs` |
Bitwise AND assignment | @a `lhs` `&=` @a `rhs` | Reference to @a `lhs` |
Bitwise OR assignment | @a `lhs` `|=` @a `rhs` | Reference to @a `lhs` |
Bitwise XOR assignment | @a `lhs` `^=` @a `rhs` | Reference to @a `lhs` |
Bitwise shift left assignment | @a `lhs` `<<=` @a `rhs` | Reference to @a `lhs` |
Bitwise shift right assignment | @a `lhs` `>>=` @a `rhs` | Reference to @a `lhs` |
@par
These can be further categorised into two types; direct assignment, which is
comprised only of the
simple assignment operator, and compound assignments,
which is the set of all other assignment operators.
@par Direct Assignment
Direct assignments replace the contents of the @b `lhs` with a copy of the @b
`rhs`:
@par
@par
The @b `rhs` side is not modified and the @b `lhs` is not read - only written
to. If the @b `rhs` type does not match the @b `lhs` type, the @b `rhs` is first
implicitly converted into a new temporary with the @b `lhs` type before being
copied into the @b `lhs`. The following combination of AX type categories are
supported for direct assignment (if an operation is not listed, it is not
supported):
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "scalar" | `=` |
@ref axscalars "scalar" |
On type mismatch, right hand side is copied and
@ref axtypeprecedence "implicitly cast" to the left hand side type. |
@ref axvecmats "vector" |
`=` |
@ref axscalars "scalar" |
Each element of the vector is set to the right hand side scalar. i.e.
`a[0] = b; ... a[n-1] = b;`
where `n` is the size of the vector. If the scalar type does not match
element type of vector, the scalar is copied and @ref axtypeprecedence
"implicitly cast" to that type. |
@ref axvecmats "vector" |
Component wise assignment i.e.
`a[0] = b; ... a[n-1] = b;`
where `n` is the size of the vector.
@code{.c}
vec3f a = 0, b = 1;
a = b;
@endcode
is equal to:
@code{.c}
vec3f a = 0, b = 1;
for (int i = 0; i < 3; ++i) a[i] = b[i];
@endcode
If the right hand side element type does not
match the left hand side element type, each element of the right hand side is
copied and @ref axtypeprecedence "implicitly cast "to the target left hand side
element type.
Operand sizes must match. |
@ref axvecmats "matrix" |
`=` |
@ref axscalars "scalar" |
Diagonal matrix construction. Each diagonal component of the left hand side
matrix is set to to the right hand side scalar. All other components are zero
initialized i.e.
@code{.c}
mat3f a;
int dim = 3, b = 1;
for (int i = 0; i < dim; ++i)
a[i] = (i % (dim+1) == 0) ? b : 0;
@endcode
Where `a` is the matrix, `dim` is the dimension of the matrix (e.g. 3 for
`mat3f`) and `b` is the scalar. If the scalar type does not match element type
of matrix, the scalar is copied and @ref axtypeprecedence "implicitly cast" to
that type. |
@ref axvecmats "matrix" |
Component wise assignment i.e.
`a[0] = b[0]; ... a[n-1] = b[n-1];`
where `n` is the @b total size of the matrix.
@code{.c}
mat4f a = 0, b = 1;
a = b;
@endcode
is equal to:
@code{.c}
mat4f a = 0, b = 1;
for (int i = 0; i < 16; ++i) a[i] = b[i];
@endcode
If the right hand side element type does not match the left hand side element
type, each element of the right hand side is copied and @ref axtypeprecedence
"implicitly cast" to the target left hand side element type.
Operand sizes must match. |
@ref axstrings "string" |
`=` |
@ref axstrings "string" |
Replaces the contents of the
left hand side string with a copy of the contents in the right hand side string.
|
@par
@code{.c}
float b;
vec3f c = 0.0f; // assign components of c (x/y/z) to an floating point literal of value 0.0f
int a = 1; // assign a from an integer literal of value 1
c = a; // assign components of c (x/y/z) to the result of float(a)
@endcode
@par Compound Assignment
Compound assignments replace the the contents of the @b `lhs` with the result of
a @ref axopbinarithmetic "binary arithmetic" operation of the @b `lhs` with the
@b `rhs`:
@par
Where @b op is one of :
`+=` |
`-=` |
`*=` |
`/=` |
`%=` |
`&=` |
`|=` |
`^=` |
`<<=` |
`>>= ` |
@par
The behaviour of a given compound assignment (for example `a += b`) is similar
to replacing the compound assignment with a @ref axopassignment
"direct assignment" followed by a @ref axopbinarithmetic "binary expression"
with the same operands and given arithmetic token (i.e. `a = a + b`). However,
compound assignments imporantly do @b not evaluate the @b `lhs` twice. This is
important when assigning to an expression which is not an attribute or local
value. The best example of this is assigning to a @ref axopunincdec "pre-crement"
operation:
@par
@code{.c}
int a = 1;
++a += 1; // equal to a = ++a + 1 which is equal to 3
@endcode
@par
The @b `rhs` side is not modified, however the @b `lhs` is both read from and
written to. Note that the arithmetic operation may cast either @b `lhs` or @b
`rhs` following the rules of AX's @ref axopbinarithmetic
"arithmetic type precedence". See the @ref axopbinarithmetic
"arithmetic operations" section for more information on arithmetic type
precedence and explanations on the above compound operators.
@par
@code{.c}
int a = 3;
a += a; // the same as a = a + a; a will be set to 6
float b = 0;
b -= a; // the same as b = b - float(a);
a *= b; // the same as a = float(a) * b; Note that although the target type is int, the binary op is performed at float precision
@endcode
@par Assignment Chains
As assignments return a @b reference to the left hand side, all assignment
operators can be @b chained. Note however that the return result will be at the
type of the left hand side. For example:
@par
Given
@par
@code{.c}
float a;
int b, c;
@endcode
@par
This
@par
@code{.c}
a = b = c = 4.5f;
@endcode
@par
is fundamentally the same as:
@par
@code{.c}
c = 4.5f; // c of type int. 4.5f will be floored (int(4.5)). c becomes 4
b = c; // b becomes 4
a = b; // a becomes float(4)
@endcode
@par
Importantly, @b `a` receives the result of any previous implicit casts to the
right hand side of its binary assignment.
@subsubsection axopbinarithmetic Arithmetic
@par
Binary arithmetic operations compute the result of a arithmetic operand token on
two inputs and returns the result. The inputs are not modified, however may be
copied and @ref axtypeprecedence "implicitly cast" to temporary values if the
types do not match. All results are returned as new temporary values. If
@b integral is explicitly stated, the operation is only valid on types which
have an integer element type and will involve and implicit cast to integer types
for floating point operands (the operation may be further restricted for
container types). Binary arithmetic operations have the following forms:
@par
Operator Name | Operator Syntax | Returns |
Addition | @a `lhs` `+` @a `rhs` | The sum of both operands |
Subtraction | @a `lhs` `-` @a `rhs` | The first operand minus the second operand |
Multiplication | @a `lhs` `*` @a `rhs` | The product of both operands |
Division | @a `lhs` `/` @a `rhs` | The first operand divided by the second operand |
Modulo | @a `lhs` `%` @a `rhs` | The @b floored modulo operator. See @ref axbinmultop "Multiplicative operands" |
Bitwise AND | @a `lhs` `&` @a `rhs` |
The @b integral bitwise AND result of each bit in the first operand applied to the bit at the same location in the second operand |
Bitwise OR | @a `lhs` `|` @a `rhs` |
The @b integral bitwise OR result of each bit in the first operand applied to the bit at the same location in the second operand |
Bitwise XOR | @a `lhs` `^` @a `rhs` |
The @b integral bitwise XOR result of each bit in the first operand applied to the bit at the same location in the second operand |
Bitwise shift left | @a `lhs` `<<` @a `rhs` |
The @b integral bitwise left shift of the first operand by the second operand |
Bitwise shift right | @a `lhs` `>>` @a `rhs` |
The @b integral bitwise right shift of the first operand by the second operand |
@anchor axbinadditiveop
@par Additive operands
Binary additive operations perform summations on the input arguments. They have
the following forms:
@par
@a `lhs` @b `+` @a `rhs` |
@a `lhs` @b `-` @a `rhs` |
@par
After @ref axtypeprecedence "implicit conversion", the result of the binary
operation with the arithmetic @b `+` (@ref axtokens "plus token") is the sum of
the operands, and the result of the binary operation with the arithmetic @b `-`
(@ref axtokens "minus token") token is the second operand subtracted from the
first operand. The following combination of AX type categories are supported for
additive operations (if an operation is not listed, it is not supported):
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "scalar" | `+` `-` | @ref axscalars "scalar" |
Returns the result of the scalar addition or subtraction. |
@ref axvecmats "vector" |
Performs per component binary operations
(after @ref axtypeprecedence "implicit conversion") with the left hand side
scalar to every element of the right hand side vector or matrix, returning a
vector or matrix.
@code{.c}
vec3f a = 2.0f;
int b = 1;
vec3f c = b + a;
@endcode
is equal to:
@code{.c}
vec3f a = 2.0f;
int b = 1;
vec3f c;
for (int i = 0; i < 3; ++i) c[i] = b + a[i];
@endcode
|
@ref axvecmats "matrix" |
@ref axvecmats "vector" |
`+` `-` |
@ref axscalars "scalar" |
Same as `scalar op vector` |
@ref axvecmats "vector" |
Performs per component binary operations (after
@ref axtypeprecedence "implicit conversion"), returning a new vector with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
vec3f a = 2, b = 1;
vec3f c = a - b;
@endcode
is equal to:
@code{.c}
vec3f a = 2, b = 1;
vec3f c;
for (int i = 0; i < 3; ++i) c[i] = a[i] - b[i];
@endcode
|
@ref axvecmats "matrix" |
`+` `-` |
@ref axscalars "scalar" |
Same as `scalar op matrix` |
@ref axvecmats "matrix" |
Performs per component binary operations (after
@ref axtypeprecedence "implicit conversion"), returning a new matrix with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
mat4f a = 0, b = 1;
mat4f c = a - b;
@endcode
is equal to:
@code{.c}
mat4f a = 0, b = 1;
mat4f c;
for (int i = 0; i < 16; ++i) c[i] = a[i] - b[i];
@endcode
|
@ref axstrings "string" |
`+` |
@ref axstrings "string" |
Performs string concatenation |
@note
Floating point addition and subtraction is communicative (`a + b = b + a`) but
is not @b necessarily associative. i.e. `(a + b) + c` is not necessarily equal
to `a + (b + c)`.
@par
Examples of validity of additive operations:
@par
@code{.c}
int a = 0;
vec2f b = 1.0f;
vec2i c = 2;
mat4d d = 0.0;
c + a; // valid vec + scalar
c + d; // invalid vec + matrix
c - b; // valid vec - scalar
@endcode
@anchor axbinmultop
@par Multiplicative operands
The binary multiplicative operands have the following forms:
@par
@a `lhs` @b `*` @a `rhs` |
@a `lhs` @b `/` @a `rhs` |
@a `lhs` @b `%` @a `rhs` |
@par
After @ref axtypeprecedence "implicit conversion", the result of the binary
operation:
- with the arithmetic @b `*` (@ref axtokens "asterisk token") is:
- the product of the operands
- @b OR performs matrix multiplication for vector and matrix types.
- with the arithmetic @b `/` (@ref axtokens "forward slash token") is the first
operand divided by the second operand.
- with the arithmetic @b `%` (@ref axtokens "percentage token") is the
@b floored modulo operations .i.e. the expression `d % D` returns the result
`D - d * floor(D/d)`. This is in contrast to truncated modulo operations
`D - d * (D/d)` where the division is truncated.
@warning
Floored modulo has been chosen to provide better expected behaviour with signed
dividends. In such cases the result will always be positive; in other words,
the sign of the result is @b always taken from the divisor. This does however
mean that the relationship properties between `%` and `/` differ when either
@b `d` or @b `D` is negative. i.e.:
`(d/D)*D + (d%D) != d`
@note
The multiplicative operation @b `*` has important behaviour for vectors and
matrix types.
@par
The following combination of AX type categories are supported for multiplicative
operations (if an operation is not listed, it is not supported):
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "scalar" | `* / %` | @ref axscalars "scalar" |
Returns the result of the scalar multiplication, division or remainder of the division respectively. |
@ref axvecmats "vector" |
Performs per component binary operations (after
@ref axtypeprecedence "implicit conversion") with the left hand side scalar to
every element of the right hand side vector. The scalar is effectively treated
as a vector of the same size as the right hand side type.
@code{.c}
vec3f a = 2.0f;
int b = 1;
vec3f c = b * a;
@endcode
is equal to:
@code{.c}
vec3f a = 2.0f;
int b = 1;
vec3f c;
for (int i = 0; i < 3; ++i) c[i] = b * a[i];
@endcode
|
`*` | @ref axvecmats "matrix" |
Performs matrix multiplication after diagonal matrix construction from the left
hand side scalar.
@code{.c}
mat3f a = 1;
float b = 1;
mat3f c = a * b;
@endcode
is equal to:
@code{.c}
mat3f a = 1;
float b = 1;
mat3f tmp = b; // diagonal matrix
mat3f c = a * tmp;
@endcode
|
@ref axvecmats "vector" |
`* / %` |
@ref axscalars "scalar" |
Same as `scalar op vector`
with the operands reversed (importantly for division and modulus) |
@ref axvecmats "vector" |
Performs per component binary operations (after
@ref axtypeprecedence "implicit conversion"), returning a new vector with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
vec3f a = 2, b = 1;
vec3f c = a * b;
@endcode
is equal to:
@code{.c}
vec3f a = 2, b = 1;
vec3f c;
for (int i = 0; i < 3; ++i) c[i] = a[i] * b[i];
@endcode
|
`*` | @ref axvecmats "matrix" |
Transforms the left hand side vector by the right hand side matrix using matrix
multiplication. This is the same as calling the @ref axtransform "transform"
function. e.g:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
b = b * a;
@endcode
is equal to:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
b = transform(b, a);
@endcode
Typically, matrix multiplication requires arguments of the same dimension sizes -
that is, a `mat4` can only be applied to a `vec4`. However, it is often useful to
be able to directly apply `mat4` transformations to `vec3` types, most often due
to positions being represented as `vec3`. `vec3 * mat4` multiplication is
supported, where by the `vec3` is extended with a `1` component and the
resulting last last component is dropped from the return
value:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
// b * a is equal to:
vec4f tmp;
tmp[0] = b[0];
tmp[1] = b[1];
tmp[2] = b[2];
tmp[3] = 1;
tmp = tmp * a;
b[0] = tmp[0];
b[1] = tmp[1];
b[2] = tmp[2];
@endcode
@b Note: Valid operand sizes and return types match those of the
@ref axtransform "transform" function i.e:
- `vec3 * mat3 = vec3`
- `vec3 * mat4 = vec3`
- `vec4 * mat4 = vec4`
|
@ref axvecmats "matrix" |
`*` |
@ref axscalars "scalar" |
Same as `scalar * matrix` |
@ref axvecmats "vector" |
Transforms the right hand side vector by the left hand side matrix using matrix
multiplication. This is the same as calling the @ref axpretransform "pretransform"
function. e.g:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
b = a * b;
@endcode
is equal to:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
b = pretransform(a, b);
@endcode
Typically, matrix multiplication requires arguments of the same dimension sizes -
that is, a `mat4` can only be applied to a `vec4`. However, it is often useful to
be able to directly apply `mat4` transformations to `vec3` types, most often due
to positions being represented as `vec3`. `mat4 * vec3` multiplication is
supported, where by the `vec3` is extended with a `1` component and the
resulting last last component is dropped from the return
value:
@code{.c}
mat4f a = identity4();
vec3f b = { 1, 2, 3 };
// a * b is equal to:
vec4f tmp;
tmp[0] = b[0];
tmp[1] = b[1];
tmp[2] = b[2];
tmp[3] = 1;
tmp = a * tmp;
b[0] = tmp[0];
b[1] = tmp[1];
b[2] = tmp[2];
@endcode
@b Note: Valid operand sizes and return types match those of the
@ref axpretransform "pretransform" function i.e:
- `mat3 * vec3 = vec3`
- `mat4 * vec3 = vec3`
- `mat4 * vec4 = vec4`
|
@ref axvecmats "matrix" |
Performs matrix multiplication and returns the matrix product, which is matrix
of matchign size and type. Operand sizes must match. e.g:
@code{.c}
mat4f a = 1, b = 2;
mat4f c = a * b;
@endcode
is equal to:
@code{.c}
mat4f a = 1, b = 2;
mat4f c;
for(int i = 0; i < 4; ++i) {
for(int j = 0; j < 4; ++j) {
for(int k = 0; k < 4; ++k) {
c[i,j] += a[i,k] * b[k,j];
}
}
}
@endcode
|
@note
Floating point multiplication is communicative (`a * b = b * a`) but is not
@b necessarily associative. i.e. `(a * b) * c` is not necessarily equal to
`a * (b * c)`.
@anchor axbinbitwiseop
@par Bitwise operands
Binary bitwise operations have the following forms:
@par
@a `lhs` @b `&` @a `rhs` |
@a `lhs` `|` @a `rhs` |
@a `lhs` @b `^` @a `rhs` |
@a `lhs` @b `<<` @a `rhs` |
@a `lhs` @b `>>` @a `rhs` |
@par
These operations only run on @b integral operands. After @ref axtypeprecedence
"implicit conversion", the result of the binary operation:
- with the @b `&` (@ref axtokens "ampersand token") is the result of the
logical AND operation on each pair of the corresponding bits of the input
operands.
- with the
`|` (@ref axtokens "pipe token") is the result of the
logical OR operation on each pair of the corresponding bits of the input
operands.
- with the @b `^` (@ref axtokens "hat token") is the result of the
logical XOR operation on each pair of the corresponding bits of the input
operands.
- with the @b `<<` (@ref axtokens "less than tokens") is the value shifted left
with zeros shifted in on the right.
- with the @b `>>` (@ref axtokens "greater than tokens") is the value shifted
right with zeros shifted in on the left.
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "intergral" |
`& | ^ << >>` |
@ref axscalars "intergral" |
Returns the result of the bitwise operation as described above. If either operand
is not an integral type (`bool` `int32` or `int64`), the program is ill formed.
|
@subsubsection axopcomparison Comparisons / Relational
@par
Binary comparison and relational operations compute the result of a equality
operand token on two inputs and returns the result. The inputs are not modified,
however may be copied and @ref axtypeprecedence "implicitly cast" to temporary
values if the types do not match. The result type of all comparison operators
is of type @b `bool`. Binary comparison operations have the following forms:
@par
Operator Name | Operator Syntax | Returns |
Equal to | @a `lhs` `==` @a `rhs` |
Returns `true` if both operands are equal |
Not equal to | @a `lhs` `!=` @a `rhs` |
Returns `true` if operands are not equal |
Less than | @a `lhs` `<` @a `rhs` |
Returns `true` the left hand side value is less than the right hand side |
Greater than | @a `lhs` `>` @a `rhs` |
Returns `true` the left hand side value is greater than the right hand side |
Less than or equal to | @a `lhs` `<=` @a `rhs` |
Returns `true` the left hand side value is less than or equal to the right hand side |
Greater than or equal to | @a `lhs` `>=` @a `rhs` |
Returns `true` the left hand side value is greater than or equal to the right hand side |
@par
The following combination of AX type categories are supported for comparison
operations (if an operation is not listed, it is not supported):
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "scalar" | `== != < > <= >=` | @ref axscalars "scalar" |
Returns the result of the scalar comparison. |
@ref axvecmats "vector" |
Performs per component comparisons (after
@ref axtypeprecedence "implicit conversion") with the lefthand side scalar to
every element of the right hand side vector or matrix and perform a logical AND
combination on the results of these comparisons. This effectively returns true
if every element of the vector or matrix is equal to the scalar.
@code{.c}
vec3f a = 2.0f;
int b = 1;
bool c = b == a;
@endcode
is equal to:
@code{.c}
vec3f a = 2.0f;
int b = 1;
bool c = a[0] == b;
for (int i = 1; i < 3; ++i) c &= a[i] == b;
@endcode
|
@ref axvecmats "matrix" |
@ref axvecmats "vector" |
`== != < > <= >=` |
@ref axscalars "scalar" |
Same as `scalar op vector` |
@ref axvecmats "vector" |
Performs binary comparison operations (after
@ref axtypeprecedence "implicit conversion") on each pair corresponding
components in the vector operands and returns true if all component pairs are
equal. Operand sizes must match.
@code{.c}
vec3f a = 1, b = 2;
bool c = a == b;
@endcode
is equal to:
@code{.c}
vec3f a = 1, b = 2;
bool c = a[0] == b[0];
for (int i = 1; i < 3; ++i) c &= a[i] == b[i];
@endcode
|
@ref axvecmats "matrix" |
`== != < > <= >=` |
@ref axscalars "scalar" |
Same as `scalar op matrix` |
@ref axvecmats "matrix" |
Performs binary comparison operations (after
@ref axtypeprecedence "implicit conversion") on each pair corresponding
components in the matrix operands and returns true if all component pairs are
equal. Operand sizes must match.
@code{.c}
mat4f a = 1, b = 2;
bool c = a == b;
@endcode
is equal to:
@code{.c}
mat4f a = 1, b = 2;
bool c = a[0] == b[0];
for (int i = 1; i < 16; ++i) c &= a[i] == b[i];
@endcode
|
@subsubsection axopbinlogical Logical
@par
Binary logical operations compute and return the result of a boolean operation
on two operands. Both operands are implicitly converted to @b `bool` types if
they are not already.
@warning
These operators are short-circuiting; for logical AND, the second operand is
only evaluated if the first is @b true. For logical OR, the second operand is
only evaluated if the first is @b false. In both cases, the first operand is
always evaluated.
@par
They have the following forms:
@par
Operator Name | Operator Syntax | Returns |
AND | @a `lhs` `&&` @a `rhs` |
Returns true if both operands are true. Otherwise, the result is false. This
operator is short-circuiting. |
Inclusive OR | @a `lhs` `||` @a `rhs` |
Returns true if either the first or the second operand is true. This operator
is short-circuiting. |
@par
The following combination of AX type categories are supported for comparison
operations (if an operation is not listed, it is not supported):
@par
Left Operand Type | Binary Op(s) | Right Operand Type | Description |
@ref axscalars "scalar" | `&& ||` | @ref axscalars "scalar" |
Returns the result of the scalar logical operation.
Both scalars are converted to bools if they are not already. |
@subsection axopunary Unary Operators
@par
Unary operators only execute on a single value. The operator token(s) may come
before or after the value, precedence of which is defined by AX's
@ref axopprecedence "operator precedence". Arithmetic, logical and
increment / decrement operators are supported.
@subsubsection axopunarithmetic Arithmetic
@par
Arithmetic unary operators are comprised of arithemtic and bitwise operators.
@par
Operator Name | Operator Syntax | Returns |
Unary plus | `+`@a `a` |
Returns the value of its operand, @b `a` |
Unary minus | `-`@a `a` |
Returns the negative representation of @b `a` |
Bitwise NOT | `~`@a `a` |
Returns the bitwise NOT (one's complement) value of @b `a`. This operator
is only valid on integral element types. |
@par
The following combination of AX type categories are supported for unary
arithmetic operations (if an operation is not listed, it is not supported):
@par
Operand Type | Binary Op(s) | Description |
@ref axscalars "scalar" | `+ - ~` |
Returns the result of the scalar unary operations. |
@ref axvecmats "vector" |
Performs per component unary operations, returning a new vector with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
vec3f a = 2;
vec3f b = -a;
@endcode
is equal to:
@code{.c}
vec3f a = 2;
vec3f b;
for (int i = 0; i < 3; ++i) b[i] = -a[i];
@endcode
@b Note: The bitwise not `~` operator is only valid on integer vector types;
`vec2i`, `vec3i` and `vec4i`.
|
@ref axvecmats "matrix" | `+ -` |
Performs per component unary operations, returning a new matrix with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
vec3f a = 2;
vec3f b = -a;
@endcode
is equal to:
@code{.c}
vec3f a = 2;
vec3f b;
for (int i = 0; i < 3; ++i) b[i] = -a[i];
@endcode
|
@subsubsection axopunlogical Logical
@par
Logical unary operators consist of a single operand.
@par
Operator Name | Operator Syntax | Returns |
Logical NOT | `!`@a `a` |
Returns true if the operand is false. Otherwise, returns false. |
@par
The following combination of AX type categories are supported for logical unary
operations (if an operation is not listed, it is not supported):
@par
Operand Type | Binary Op(s) | Description |
@ref axscalars "scalar" | `!` |
Returns the result of the scalar logical operation.
The scalar operand is converted to a bool if it is not already. |
@ref axvecmats "vector" |
Performs per component unary operations, returning a new vector with each
element holding the corresponding result. Operand sizes must match.
@code{.c}
vec3i a = 2;
vec3i b = !a;
@endcode
is equal to:
@code{.c}
vec3i a = 2;
vec3i b;
for (int i = 0; i < 3; ++i) b[i] = !a[i];
@endcode
@b Note: The logical not `!` operator is only valid on integer vector types;
`vec2i`, `vec3i` and `vec4i`.
|
@subsubsection axopunincdec Increment / Decrement
@par
Increment/decrement operators increment or decrement the value of a scalar, or
each component of a vector or matrix. They have the following forms:
@par
Operator Name | Operator Syntax | Returns |
Pre-increment | `++`@a `a` | Returns a reference to the incremented result. |
Post-increment | @a `a``++` | Returns a copy of the incremented result. |
Pre-decrement | `--`@a `a` | Returns a reference to the decremented result. |
Post-decrement | @a `a``--` | Returns a copy of the decremented result. |
@par
Importantly, pre evaluation returns a reference to the value operand, where as
post evaluation returns a copy of the operand. The following combination of AX
type categories are supported for logical unary operations (if an operation is
not listed, it is not supported):
@par
Operand Type | Binary Op(s) | Description |
@ref axscalars "scalar" |
`++(pre) (post)++` `--(pre) (post)--` |
Returns the result (reference or copy) of the scalar
increment or decrement operation.\n
@b Note: boolean incrementation and decrementation is not supported. Only
`int32`, `int64`, `float` and `double` types are valid. |
@subsection axopaccess Container Access
@par
Container access operators are valid on AX Vector and matrix types. They have
the following forms:
@par
Operator Name | Operator Syntax | Returns |
Dot Component Access |
`vector` . @a `component` |
Reference to component @a `component` of @a `vector` |
Container Component Access |
`container` [ @a `exp` ] |
Reference to component at index @a `exp` of @a `container` |
Matrix Component Access |
`matrix` [ @a `exp1`, `exp2` ] |
Reference to component in row `exp1`, column `exp2`.
Also equal to returning a reference to component at index: `[exp1 * dimension + exp2]`
of @a `matrix` where @a `dimension` is the the size of the matrix |
@par
As data is stored contiguously for both vectors and matrices, the
`[]`
operator is valid for both types. However the
. operator is only valid
on vector types, and the
`[,]` operator is only valid on matrix types.
As return values for these operators are references, any modifications through
@ref axopassignment "assignments" will update the stored value in the
container.
@par . operator
The dot operator
. is only valid for vector types. It has the form:
@par
@a `vector` . @a `component` |
@par
Where component is one of the below supported component letters. It only provides
access for the first three elements of any vector (for @b `vec2` types, only the
first 2 components are accessible). Accessing a vector using the dot operator is
fundamentally the same as using the
`[]` operator with the corresponding
integer index.
@par
@code{.c}
vec4i a = { 6, 7, 8, 9 };
int b = a.z; // get the the third elements value (8)
@endcode
@f[
A=\begin{bmatrix}
a_{0}\quad a_{1}\quad a_{2}
\end{bmatrix}
@f]
| Component | Access Index | Result |
|:-----------:|:--------------:|:-------------:|
| @b `A.x` | 0 | @f$ a_{0} @f$ |
| @b `A.r` | 0 | @f$ a_{0} @f$ |
| @b `A.y` | 1 | @f$ a_{1} @f$ |
| @b `A.g` | 1 | @f$ a_{1} @f$ |
| @b `A.z` | 2 | @f$ a_{2} @f$ |
| @b `A.b` | 2 | @f$ a_{2} @f$ |
@par
This operator can provide more immediately readability when working with
expressions that focus on colour or Cartesian coordinate systems.
@anchor axvecaccessop
@par [] operator
The
`[]` operator is valid for both vectors and matrices. It has the form:
@par
@par
@a `exp` is expected to evaluate to a value which is castable to an integer
following the @ref axopassignment "assignment casting" rules and falls within
the valid size of the @a `container`.
No bounds checking is performed.
@par
As matrices are also an array of contigous values, they can be accessed in the
same way. This can make some operations much simplier; a typical example being
iterating through all matrix elements and performing some logic:
@par
@code{.c}
vec3f a = 0;
a[0] = 1; // valid
a[""] = 1; // invalid - can't cast to int
a[1.2f] = 1; // valid - float exp will be clamped to an int
a[a[0]] = 1; // valid - use the value of a[0] to index into a
// assign each element its actual index value i.e. b[5] = 5;
mat4d b;
for (int i = 0; i < 16; ++i) b[i] = i;
@endcode
@anchor axmataccessop
@par [,] operator
The
`[,]` operator is only valid for matrices. It has the form:
@par
@a `matrix` [`exp1`, `exp2`] |
@par
@a `exp1` and @a `exp2` are expected to evaluate to a value which is castable to
an integer following the @ref axopassignment "assignment casting" rules and
falls within the valid size of the @a `matrix` dimensions.
No bounds checking
is performed. In general it is more readable to use this operator when
accessing specific elements, where @b `exp1` is the row index and @b `exp2` is
the column index. The tables below show the different access patterns for
@b `3x3` and @b `4x4` matrices.
@par
@code{.c}
mat3f a;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
a[i,j] = i*j; // assign matrix element the product of its index
@endcode
@par
@f[
A=\begin{bmatrix}
a_{00}\quad a_{01}\quad a_{02}\\
a_{10}\quad a_{11}\quad a_{12}\\
a_{20}\quad a_{21}\quad a_{22}\\
\end{bmatrix}
@f]
|
|
@f[
A=\begin{bmatrix}
a_{00}\quad a_{01}\quad a_{02}\quad a_{03}\\
a_{10}\quad a_{11}\quad a_{12}\quad a_{13}\\
a_{20}\quad a_{21}\quad a_{22}\quad a_{23}\\
a_{30}\quad a_{31}\quad a_{32}\quad a_{33}\\
\end{bmatrix}
@f]
|
Access [a] |
Access [a,b] |
Result |
|
Access [a] |
Access [a,b] |
Result |
A[0] |
A[0,0] |
@f$ a_{00} @f$ |
A[0] |
A[0,0] |
@f$ a_{00} @f$ |
A[1] |
A[0,1] |
@f$ a_{01} @f$ |
A[1] |
A[0,1] |
@f$ a_{01} @f$ |
A[2] |
A[0,2] |
@f$ a_{02} @f$ |
A[2] |
A[0,2] |
@f$ a_{02} @f$ |
A[3] |
A[1,0] |
@f$ a_{10} @f$ |
A[3] |
A[0,3] |
@f$ a_{03} @f$ |
A[4] |
A[1,1] |
@f$ a_{10} @f$ |
A[4] |
A[1,0] |
@f$ a_{10} @f$ |
A[5] |
A[1,2] |
@f$ a_{12} @f$ |
A[5] |
A[1,1] |
@f$ a_{11} @f$ |
A[6] |
A[2,0] |
@f$ a_{20} @f$ |
A[6] |
A[1,2] |
@f$ a_{12} @f$ |
A[7] |
A[2,1] |
@f$ a_{21} @f$ |
A[7] |
A[1,3] |
@f$ a_{13} @f$ |
A[8] |
A[2,2] |
@f$ a_{22} @f$ |
A[8] |
A[2,0] |
@f$ a_{20} @f$ |
|
A[9] |
A[2,1] |
@f$ a_{21} @f$ |
A[10] |
A[2,2] |
@f$ a_{22} @f$ |
A[11] |
A[2,3] |
@f$ a_{23} @f$ |
A[12] |
A[3,0] |
@f$ a_{30} @f$ |
A[13] |
A[3,1] |
@f$ a_{31} @f$ |
A[14] |
A[3,2] |
@f$ a_{32} @f$ |
A[15] |
A[3,3] |
@f$ a_{33} @f$ |
@subsection axopother Other
@par
A number of uncategorised operators that are more general are documented in this
section. They have the following forms:
@par
Operator Name | Operator Syntax | Returns |
Call / Explicit Cast |
`a``(...)` |
Returns the result of a function call or inbuilt explicit cast |
Comma |
`a`, `b` |
Returns the value of @b `b` after chained evaluation |
Ternary Conditional |
`a` `?` `b` `:``c` |
@b `b` if @b `a` is @b true, @b `c` otherwise. |
Vector/Matrix Initializer |
`{` `a, ` `b ``... }` |
Returns a temporary vector or matrix |
@par Call / Explicit Cast
The function call operator provides the ability to invoke functions @b or invoke
a supported explicit cast. It has the form:
@par
@par
Where @b `func` is a function name @b or a valid explicit cast typename. Explicit
casts are only supported for @ref axscalars "scalar types". For example:
@code{.c}
int a = int(1.1f);
vec3f b = 1.0f;
a = int(b.x);
@endcode
@par
Whilst explicit casts take only a single argument, the operator itself supports
multiple or empty arguments for function calls. Whe multiple arguments are
provided, they are parsed from first to last and processed in reverse order.
@b However, the order of runtime evaluation is @b unspecified i.e. they may
evaluate in any order. Each function parameter is initialized with its
corresponding argument after following implicit conversion of assignments where
necessary.
@par
See the @ref axfunctions "function documentation" for more information on
functions.
@par Comma
The Comma operator has the form:
@par
@par
Where @b `a` and @b `b` are expressions. Chained expressions evaluate each
expression in syntactical order (first to last), discarding any return value
except for the last expression, which is returned from the entire list. Note that
although return values for any expression but the last are discarded, their side
effects are still applied before subsequent expression are evaluated. For
example:
@code{.c}
int a = 5;
a-=1, a+=2; // a = 6
a = a--, ++a; // exp1: a = a--, exp2: ++a. a = 7
@endcode
@note
Although you may consider the comma operator as a type of binary operator, it
is fundamentally different as the types either side of the operator do @b not
interact. Each expression in the comma operator is evaluated independently of
all other expressions.
@anchor axopternary
@par Ternary Conditional
The ternary conditional operator can be thought of as an in-line @ref axbranching
"if-else statement" that returns a value, where each of the if/else branches has
a single expression that will be returned. It has the form:
@par
Where @b `a` is the condition, evaluated and converted to @b bool, and @b `b` and @b
`c` are expressions of the same or implicit-castable types. @b `b` is evaluated and
returned if the condition is @b true, @b `c` is evaluated and returned if the
condition is @b false. Only the expression out of @b `b` and @b `c` that is
returned will be evaluated. Expressions with no return value (a.k.a @b `void`)
are supported as long as @b `b` and @b `c` are both of this type.
@par
Also supported is the 'Elvis' operator,
`a` `?` `:` `c` (or `?:`).
Here, @b `a` is evaluated once, and if when converted to @b bool is @b true, @b `a` is
returned, otherwise @b `c` is evaluated and returned. In this case, @b `a` and @b
`c` must be the same or implicit-castable types, and both implicit-castable to @b bool.
@anchor axvecmatinit
@par Vector/Matrix Initializer
@ref axvecmats "Vectors and matrices" can be represented as local variables (e.g.
@b `vec3f`), external attributes or parameters (e.g. @b `vec3f@attrib`) or as
temporary values using the vector/matrix initializer syntax. This operator has
the form:
@par
@par
Where @b `a` and @b `b` are expressions returning a scalar value. When this
operator is invoked with a specific number of arguments, a vector or matrix is
implicitly created with a copy of those arguments which can be assigned to an
existing container of matching size. Expression in the operator are evaluated
from first to last. This operator is only valid with argument lists of sizes
2, 3, 4, 9 and 16, which implicitly represent
vec2, vec3, vec4, mat3, and
@b mat4 types respectively. For example:
@code{.c}
vec3f a = { 1.0f, 2.0f, 3.0f }; // right hand side of assignment creates a vec3f
vec3f b = dot(a, { a[0], 5.0, 6.0 }); // second argument creates a vec3d
@endcode
@note
In the above examples, the value of
`a[0]` is copied into the initializer
operator.
@par Initialization Type
A mixture of arbitrary scalar types can be used in the initializer operator e.g:
@code{.c}
mat3f a = {
true, 0s, 0,
0l, 0.0f, 0.0,
false, 1, 2
};
@endcode
@par
The temporary container evaluates all expressions and uses the @b highest ranking
type (as defined by @ref axtypeprecedence "arithmetic type precedence") to infer
the element type of the container, implicitly casting all other types to that
type where necessary. In the above example, the temporary created forms a
@b `mat3d`, before being implicitly cast to a @b `mat3f` due to the assignment.
@subsection axopprecedence Operator Precedence
@par
The below table shows the precedence and associativity of all AX operators.
Operators are listed in descending precedence.
@par
Precedence |
Operator |
Description |
Associativity |
1 |
() |
Parenthesis |
Left-to-right |
2 |
a++ a\-\- |
Suffix/postfix @ref axopunincdec |
type() |
@ref axopother "Functional cast" |
a() |
@ref axopother "Function call" |
a[] . |
@ref axopaccess |
3 |
++a \-\-a |
Prefix @ref axopunincdec |
Right-to-left |
+a -a |
Unary @ref axopbinarithmetic "plus and minus" |
! ~ |
@ref axopbinlogical NOT and @ref axopunlogical NOT |
4 |
a*b a/b a%b |
@ref axopbinarithmetic "Multiplication, division, and remainder" |
Left-to-right |
5 |
a+b a−b |
@ref axopbinarithmetic "Addition and subtraction" |
6 |
<< >> |
@ref axopbinarithmetic "Bitwise left shift and right shift" |
7 |
< <= |
For @ref axopcomparison operators < and ≤ respectively |
> >= |
For @ref axopcomparison operators > and ≥ respectively |
8 |
== != |
For @ref axopcomparison operators = and ≠ respectively |
9 |
& |
@ref axopbinarithmetic AND |
10 |
^ |
@ref axopbinarithmetic XOR (exclusive or) |
11 |
| |
@ref axopbinarithmetic OR (inclusive or) |
12 |
&& |
@ref axopbinlogical AND |
13 |
|| |
@ref axopbinlogical OR |
14 |
a?b:c |
@ref axopternary "Ternary operator" |
Right-to-left |
= |
@ref axopassignment "Direct assignment" |
+= -= |
@ref axopassignment "Compound assignment" by sum and difference |
*= /= %= |
@ref axopassignment "Compound assignment" by product, quotient, and remainder |
<<= >>= |
@ref axopassignment "Compound assignment" by bitwise left shift and right shift |
&= ^= |= |
@ref axopassignment "Compound assignment" by bitwise AND, XOR, and OR |
15 |
, |
@ref axopother "Comma" |
Left-to-right |
@par
Note that associativity of unary operators is redundant (unary prefix operators
are always right-to-left and unary postfix operators are always left-to-right).
Operators that have the same precedence are bound to their arguments in the
direction of their associativity. For example, the expression
`a = b = c` is
parsed as
`a = (b = c)`, and not as
`(a = b) = c` because of right-to-left
associativity of assignment, but
`a + b − c` is parsed
`(a + b) − c`
and not
`a + (b − c)` because of left-to-right associativity of addition
and subtraction. @b Note that this is based off the
C++ operator precedence.
@section axtokens Tokens
@subsection axvaridentifiers Variable Identifiers
@subsection axliterals Literals
@par
Literals can be thought of as constant values that are known at compile time. e.g.
@code
i@a = 1; // the number 1 is a literal
@endcode
As AX supports different bit width scalar literals, it also supports suffix
literal tokens which, when using with a scalar literal, represent a literal of a
specific size. AX also supports implicit casting for all @ref axopassignment
"assignment" and @ref axopbinarithmetic "arithmetic" operations, so any literal
can be used to assign any scalar a value.
@par
Type | Literal Tokens |
`bool` | Tokens @b `true` and @b `false` |
`int32` | No suffix, automatically infered from integral literals |
`int64` | The letter @b `l` e.g. @code int64 a = 10l; @endcode |
`float` | The letter @b `f` e.g. @code float a = 0.0f; @endcode |
`double` | No suffix, automatically infered from floating point literals. |
`string` | Character strings wrapped in double quotes @b \" @b \" |
@par
@subsection axcomments Comments
@subsection axkeywords Keywords
@subsection axreserved Reserved Keywords
@section axsyntax Syntax
@subsection axattribaccess Attribute Access
@code{.c}
float@surface = float@density;
@endcode
@par
Assignment operations determine which volumes will be executed over. Here, the
AX program will only execute on the @b surface VDB whilst retaining access to
all available VDBs. There exists a number of possible states of both the
@b surface and @b density
volumes.
- Volumes have the same transform and the same topology
- Volumes have the same transform, but different topologies
- Volumes have different transforms, but the same topology
- Volumes have different transforms and different topologies
@par Matching Transforms
In the simplest case, all volumes processed have the same transform. Active
voxels in the @b surface VDB will receive a value sampled (@ref axattribaccess
"point sampled") at the corresponding @b index space position in the @b density
VDB. As the transforms match, this is guaranteed to be at the same @b world
space position. The topology of the @b density VDB does not influence which
values are assigned, AX simply queries the @b density VDB at the computed
position and uses whatever value is returned. This may be the VDB @b Background
value.
@par Different Transforms
When transforms differ, AX performs coordinate transformations from the source
VDB to the target VDB. Using some simple pseudo code, we can represent each
voxel's coordinate query:
@code{.unparsed}
foreach(ActiveVoxel in surface)
SurfaceIndexSpaceCoord = ActiveVoxel.indexSpaceCoordinate();
SurfaceWorldSpaceCoord = surface.transformToWorld(SurfaceIndexSpaceCoord);
DensityIndexSpaceCoord = density.transformToIndex(SurfaceWorldSpaceCoord);
ActiveVoxel = density.getValue(DensityIndexSpaceCoord);
@endcode
done()
-->
@subsection axexternalaccess External Parameter Access
@subsection axdecls Declarations
@subsection axscopes Scopes
@subsection axbranching Branching (if / else)
@subsection axloops Loops
@section axfunctions Functions
@par
For a list of all functions, see the @ref axfunctionlist "AX Function List".
@par
AX supports a variety of mathematical, string, utility and custom function calls
which can be used from within an AX program. Native function support is constantly
growing and we encourage users who wish to see particular methods exposed and
supported natively by AX to contact us. Whilst we can't guarantee
to accommodate every function, these requests provide the developers insights into
how the language is being used.
@par Function Execution Context
Some functions depend on the currently processing @ref axexecutionxcontext to
work correctly and will fail if called from an "invalid" context. Specifically,
there exists functions which operate on OpenVDB points or OpenVDB volume
grids and interact with the point or voxel data. Some of these methods are
incompatible for the other grid type (for example, @ref axdeletepoint
"deleting a point from a Point Data Grid") and will result in the compilation
failure of an AX program if an attempt is made it use them.
@subsection axuserfunctions User Functions
@par
User function declarations are not currently supported, but exist on the
AX Roadmap as a near future feature.
@section axvexsupport AX VEX Support
*/