You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After spending some time with GLM and GLSL, I'm considering to change the API for binary operations on vectors and other structures. Currently the functions such as dot(), cross() and angle() are implemented as static member functions, e.g.:
Vector3 a, b;
Float x = Vector3::dot(a, b);
Complex c, d;
Rad angle = Complex::angle(c, d);
My proposed alternative is to make dot(), cross(), angle() free functions in Math namespace (also to be more in line with other math functions, such as Math::abs(), Math::sin(), Math::lerp() etc.), so the above example would change to this:
Vector3 a, b;
Float x = Math::dot(a, b);
Complex c, d;
Rad angle = Math::angle(c, d);
The original approach has a few downsides:
the calls are more verbose than necessary (dot() could easily be free function without any ambiguity for the compiler)
there is redundant and/or confusing information (is it important that I'm calling Vector3::dot() and not e.g. Math::Vector<3, Float>::dot() or Color3::dot()? Is there any difference? Can I call e.g. Quaternion::dot() with the same arguments?)
But there are also upsides:
IDE autocompletion: writing Vector3::a| has clear intent and the autocompletion will suggest Vector3::angle() as first or second entry. From Math::a| (or even a|) the intent is not clear at all.
Compiler diagnostics: when the user mistakenly calls Vector3::dot() with Vector2 and Vector3, the compiler will helpfully suggest that the first type doesn't match. When dot() would be free function, the compiler has ~10 overloads to select from, overwhelming the user with error log.
Readability: e.g. "the operation does a dot product of two quaternions" vs. "the operation does a dot product of variables a and b, which are... quaternions?!"
Yes or no?(As always, if I make the change, the original API will stay there for some time, marked as deprecated, allowing the users to move to the new syntax when they want to)
Vector constructor helpers
Besides binary operations there are various helpers for constructing vectors, mainly to help with transformations, such as Vector*::xAxis(), Vector*::xScale() etc. Currently these are also represented as static member functions:
The enclosing class specifies dimensionality of the output (Vector3::yAxis() results in {0, 1, 0} while Vector2::yAxis() results in {0, 1}, so the extraction to free functions will not be trivial. My proposed solution is to make Math::xAxis() etc. return some "meta-type", which then would be recognized by Vector2/Vector3 constructor and extended to proper dimension count. The above example would then look like this:
This looks simpler, slightly easier to write and doesn't carry redundant or potentially confusing information. There are two issues, though:
The returned type will be some implementation-defined (and probably undocumented) meta-type, which might hurt in combination with auto:
auto x1 = Math::xAxis();
x1.length(); // Error: length() is not a member of Math::Implementation::AxisType
Vector2 x2 = Math::xAxis();
x2.length(); // ok
Calling Vector2::xAxis() has well-defined underlying type (Vector2 is typedef for Math::Vector2<Float>), but what is the underlying type of Math::xAxis()? Implicit conversions would be disallowed, like with other vector types, which might be annoying in some cases. On the other side, allowing implicit conversions might be even more annoying.
// this somehow has to work, no matter what
Vector2 a = Math::xAxis();
Vector3i x = Math::xAxis();
// error about conversion of int to float would be annoying here
Vector2 y = Math::yAxis(1);
// implicit conversion and precision loss would be annoying here
Vector3i z = Math::zScale(1.5f);
Yes or no? If yes, keep then the original Vector2::xAxis() etc. functions (as they have well-defined no-surprise behavior), or also mark them for deprecation and removal?
The text was updated successfully, but these errors were encountered:
@LB-- Thanks for the input. Yup, that's also possible, but I'm trying to avoid ambiguity/confusion by having exactly one way to do given operation. Currently, two functions with the same name in different namespace mean that they are doing something slightly different (for example, Math::max() vs. Vector3::max()).
Vector operations
After spending some time with GLM and GLSL, I'm considering to change the API for binary operations on vectors and other structures. Currently the functions such as
dot()
,cross()
andangle()
are implemented as static member functions, e.g.:My proposed alternative is to make
dot()
,cross()
,angle()
free functions inMath
namespace (also to be more in line with other math functions, such asMath::abs()
,Math::sin()
,Math::lerp()
etc.), so the above example would change to this:The original approach has a few downsides:
dot()
could easily be free function without any ambiguity for the compiler)Vector3::dot()
and not e.g.Math::Vector<3, Float>::dot()
orColor3::dot()
? Is there any difference? Can I call e.g.Quaternion::dot()
with the same arguments?)But there are also upsides:
Vector3::a|
has clear intent and the autocompletion will suggestVector3::angle()
as first or second entry. FromMath::a|
(or evena|
) the intent is not clear at all.Vector3::dot()
withVector2
andVector3
, the compiler will helpfully suggest that the first type doesn't match. Whendot()
would be free function, the compiler has ~10 overloads to select from, overwhelming the user with error log.Yes or no? (As always, if I make the change, the original API will stay there for some time, marked as deprecated, allowing the users to move to the new syntax when they want to)
Vector constructor helpers
Besides binary operations there are various helpers for constructing vectors, mainly to help with transformations, such as
Vector*::xAxis()
,Vector*::xScale()
etc. Currently these are also represented as static member functions:The enclosing class specifies dimensionality of the output (
Vector3::yAxis()
results in{0, 1, 0}
whileVector2::yAxis()
results in{0, 1}
, so the extraction to free functions will not be trivial. My proposed solution is to makeMath::xAxis()
etc. return some "meta-type", which then would be recognized byVector2
/Vector3
constructor and extended to proper dimension count. The above example would then look like this:This looks simpler, slightly easier to write and doesn't carry redundant or potentially confusing information. There are two issues, though:
auto
:Vector2::xAxis()
has well-defined underlying type (Vector2
is typedef forMath::Vector2<Float>
), but what is the underlying type ofMath::xAxis()
? Implicit conversions would be disallowed, like with other vector types, which might be annoying in some cases. On the other side, allowing implicit conversions might be even more annoying.Yes or no? If yes, keep then the original
Vector2::xAxis()
etc. functions (as they have well-defined no-surprise behavior), or also mark them for deprecation and removal?The text was updated successfully, but these errors were encountered: