Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Philip Trettner
typed-geometry
Commits
a61b80fc
Commit
a61b80fc
authored
Dec 22, 2018
by
Philip Trettner
Browse files
more work on vector, type and detail
parent
770c41fe
Changes
26
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
a61b80fc
...
...
@@ -27,3 +27,7 @@ Why not `tm::vec3`?
## Dependencies
None.
## TODO
*
Benchmark how compile times are affected by includes
src/tg/detail/constants.hh
View file @
a61b80fc
...
...
@@ -2,8 +2,6 @@
#include
<limits>
#include
"types.hh"
namespace
tg
{
template
<
class
T
>
...
...
@@ -15,4 +13,4 @@ constexpr T min = std::numeric_limits<T>::min();
template
<
class
T
>
constexpr
T
max
=
std
::
numeric_limits
<
T
>::
max
();
}
}
// namespace tg
src/tg/detail/io.hh
0 → 100644
View file @
a61b80fc
#pragma once
#include
<iostream>
#include
"../types/types.hh"
namespace
tg
{
}
\ No newline at end of file
src/tg/detail/macros.hh
0 → 100644
View file @
a61b80fc
#pragma once
// ================= OPERATORS =================
// ================= TRAITS =================
#define TG_DEFINE_BOOL_TRAIT(trait, default_val) \
template <class T> \
struct trait##_t \
{ \
static constexpr bool value = default_val; \
}; \
template <class T> \
constexpr bool trait = trait##_t<T>::value // enforce ;
#define TG_DEFINE_TYPE_TRAIT(trait, default_type) \
template <class T> \
struct trait##_t \
{ \
using type = default_type; \
}; \
template <class T> \
using trait = typename trait##_t<T>::type // enforce ;
#define TG_ADD_BOOL_TRAIT(trait, type, val) \
template <> \
struct trait##_t<type> \
{ \
static constexpr bool value = val; \
} // enforce ;
#define TG_ADD_TYPE_TRAIT(trait, ttype, result_type) \
template <> \
struct trait##_t<ttype> \
{ \
using type = result_type; \
} // enforce ;
#define TG_INHERIT_TRAITS_D(ttype) \
template <int D, class ScalarT> \
struct is_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_signed_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_signed_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_unsigned_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_unsigned_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_floating_point_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_floating_point<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct fractional_result_t<ttype<D, ScalarT>> \
{ \
using type = ttype<D, fractional_result<ScalarT>>; \
} // enforce ;
#define TG_INHERIT_TRAITS_FIELD_D(ttype) \
TG_INHERIT_TRAITS_D(ttype); \
template <int D, class ScalarT> \
struct has_multiplication_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = has_multiplication<ScalarT>; \
}; // enforce ;
src/tg/detail/op_helper.hh
0 → 100644
View file @
a61b80fc
#pragma once
namespace
tg
{
namespace
detail
{
template
<
class
ScalarT
>
constexpr
ScalarT
add
(
ScalarT
a
,
ScalarT
b
)
{
return
a
+
b
;
}
template
<
class
ScalarT
>
constexpr
ScalarT
sub
(
ScalarT
a
,
ScalarT
b
)
{
return
a
-
b
;
}
template
<
class
ScalarT
>
constexpr
ScalarT
neg
(
ScalarT
a
)
{
return
-
a
;
}
template
<
class
ScalarT
>
constexpr
ScalarT
mul
(
ScalarT
a
,
ScalarT
b
)
{
return
a
*
b
;
}
template
<
class
ScalarT
>
constexpr
bool
eq
(
ScalarT
a
,
ScalarT
b
)
{
return
a
==
b
;
}
constexpr
bool
logic_and
(
bool
a
,
bool
b
)
{
return
a
&&
b
;
}
constexpr
bool
logic_or
(
bool
a
,
bool
b
)
{
return
a
||
b
;
}
constexpr
bool
logic_not
(
bool
a
)
{
return
!
a
;
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
1
,
A
>&
r
,
vec
<
1
,
B
>
const
&
a
,
vec
<
1
,
C
>
const
&
b
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
,
b
.
x
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
2
,
A
>&
r
,
vec
<
2
,
B
>
const
&
a
,
vec
<
2
,
C
>
const
&
b
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
,
b
.
x
);
r
.
y
=
f
(
a
.
y
,
b
.
y
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
3
,
A
>&
r
,
vec
<
3
,
B
>
const
&
a
,
vec
<
3
,
C
>
const
&
b
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
,
b
.
x
);
r
.
y
=
f
(
a
.
y
,
b
.
y
);
r
.
z
=
f
(
a
.
z
,
b
.
z
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
4
,
A
>&
r
,
vec
<
4
,
B
>
const
&
a
,
vec
<
4
,
C
>
const
&
b
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
,
b
.
x
);
r
.
y
=
f
(
a
.
y
,
b
.
y
);
r
.
z
=
f
(
a
.
z
,
b
.
z
);
r
.
w
=
f
(
a
.
w
,
b
.
w
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
1
,
A
>&
r
,
vec
<
1
,
B
>
const
&
a
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
2
,
A
>&
r
,
vec
<
2
,
B
>
const
&
a
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
);
r
.
y
=
f
(
a
.
y
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
3
,
A
>&
r
,
vec
<
3
,
B
>
const
&
a
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
);
r
.
y
=
f
(
a
.
y
);
r
.
z
=
f
(
a
.
z
);
}
template
<
class
A
,
class
B
,
class
C
,
class
F
>
constexpr
void
apply
(
vec
<
4
,
A
>&
r
,
vec
<
4
,
B
>
const
&
a
,
F
&&
f
)
{
r
.
x
=
f
(
a
.
x
);
r
.
y
=
f
(
a
.
y
);
r
.
z
=
f
(
a
.
z
);
r
.
w
=
f
(
a
.
w
);
}
template
<
class
R
,
int
D
,
class
A
,
class
B
,
class
F
>
constexpr
vec
<
D
,
R
>
compwise
(
vec
<
D
,
A
>
const
&
a
,
vec
<
D
,
B
>
const
&
b
,
F
&&
f
)
{
vec
<
D
,
R
>
r
;
apply
(
r
,
a
,
b
,
f
);
return
r
;
}
template
<
class
R
,
int
D
,
class
A
,
class
B
,
class
F
>
constexpr
vec
<
D
,
R
>
compwise
(
vec
<
D
,
A
>
const
&
a
,
B
b
,
F
&&
f
)
{
vec
<
D
,
R
>
r
;
apply
(
r
,
a
,
vec
<
D
,
B
>
(
b
),
f
);
return
r
;
}
template
<
class
R
,
int
D
,
class
A
,
class
F
>
constexpr
vec
<
D
,
R
>
compwise
(
vec
<
D
,
A
>
const
&
a
,
F
&&
f
)
{
vec
<
D
,
R
>
r
;
apply
(
r
,
a
,
f
);
return
r
;
}
template
<
class
ScalarT
,
class
F
>
constexpr
ScalarT
reduce
(
vec
<
1
,
ScalarT
>
const
&
v
,
F
&&
f
)
{
return
v
.
x
;
}
template
<
class
ScalarT
,
class
F
>
constexpr
ScalarT
reduce
(
vec
<
2
,
ScalarT
>
const
&
v
,
F
&&
f
)
{
return
f
(
v
.
x
,
v
.
y
);
}
template
<
class
ScalarT
,
class
F
>
constexpr
ScalarT
reduce
(
vec
<
3
,
ScalarT
>
const
&
v
,
F
&&
f
)
{
return
f
(
f
(
v
.
x
,
v
.
y
),
v
.
z
);
}
template
<
class
ScalarT
,
class
F
>
constexpr
ScalarT
reduce
(
vec
<
4
,
ScalarT
>
const
&
v
,
F
&&
f
)
{
return
f
(
f
(
v
.
x
,
v
.
y
),
f
(
v
.
z
,
v
.
w
));
}
}
// namespace detail
}
// namespace tg
src/tg/detail/scalar_math.hh
View file @
a61b80fc
#pragma once
#include
<cmath>
#include
<type_traits>
#include
"types.hh"
#include
"../detail/traits.hh"
#include
"../types/scalar.hh"
// TODO:
// proper f8, f16
...
...
@@ -26,17 +28,39 @@ constexpr f32 abs(f32 v) { return v < 0 ? -v : v; }
constexpr
f64
abs
(
f64
v
)
{
return
v
<
0
?
-
v
:
v
;
}
f32
sin
(
f32
v
)
{
return
std
::
sin
(
v
);
}
f64
sin
(
f64
v
)
{
return
std
::
sin
(
v
);
}
inline
f32
sin
(
f32
v
)
{
return
std
::
sin
(
v
);
}
inline
f64
sin
(
f64
v
)
{
return
std
::
sin
(
v
);
}
f32
cos
(
f32
v
)
{
return
std
::
cos
(
v
);
}
f64
cos
(
f64
v
)
{
return
std
::
cos
(
v
);
}
inline
f32
cos
(
f32
v
)
{
return
std
::
cos
(
v
);
}
inline
f64
cos
(
f64
v
)
{
return
std
::
cos
(
v
);
}
f32
sqrt
(
f32
v
)
{
return
std
::
sqrt
(
v
);
}
f64
sqrt
(
f64
v
)
{
return
std
::
sqrt
(
v
);
}
inline
f32
sqrt
(
f32
v
)
{
return
std
::
sqrt
(
v
);
}
inline
f64
sqrt
(
f64
v
)
{
return
std
::
sqrt
(
v
);
}
inline
f32
pow
(
f32
b
,
f32
e
)
{
return
std
::
pow
(
b
,
e
);
}
inline
f32
pow
(
f32
b
,
i32
e
)
{
return
std
::
pow
(
b
,
e
);
}
inline
f64
pow
(
f64
b
,
f64
e
)
{
return
std
::
pow
(
b
,
e
);
}
inline
f64
pow
(
f64
b
,
i32
e
)
{
return
std
::
pow
(
b
,
e
);
}
template
<
class
T
,
class
=
std
::
enable_if_t
<
has_multiplication
<
T
>
>>
constexpr
T
pow2
(
T
const
&
v
)
{
return
v
*
v
;
}
template
<
class
T
,
class
=
std
::
enable_if_t
<
has_multiplication
<
T
>
>>
constexpr
T
pow3
(
T
const
&
v
)
{
return
v
*
v
*
v
;
}
template
<
class
T
,
class
=
std
::
enable_if_t
<
has_multiplication
<
T
>
>>
constexpr
T
pow4
(
T
const
&
v
)
{
return
(
v
*
v
)
*
(
v
*
v
);
}
template
<
class
T
,
class
=
std
::
enable_if_t
<
has_multiplication
<
T
>
>>
constexpr
T
pow5
(
T
const
&
v
)
{
return
(
v
*
v
)
*
(
v
*
v
)
*
v
;
}
f32
pow
(
f32
b
,
f32
e
)
{
return
std
::
pow
(
b
,
e
);
}
f32
pow
(
f32
b
,
i32
e
)
{
return
std
::
pow
(
b
,
e
);
}
f64
pow
(
f64
b
,
f64
e
)
{
return
std
::
pow
(
b
,
e
);
}
f64
pow
(
f64
b
,
i32
e
)
{
return
std
::
pow
(
b
,
e
);
}
}
// namespace tg
src/tg/detail/traits.hh
View file @
a61b80fc
#pragma once
#include
"types.hh"
#include
"
../types/
types.hh"
namespace
tg
{
...
...
@@ -11,14 +11,65 @@ namespace tg
static constexpr bool value = default_val; \
}; \
template <class T> \
constexpr bool trait = trait##_t<T>::value
constexpr bool trait = trait##_t<T>::value // enforce ;
#define TG_DEFINE_TYPE_TRAIT(trait, default_type) \
template <class T> \
struct trait##_t \
{ \
using type = default_type; \
}; \
template <class T> \
using trait = typename trait##_t<T>::type // enforce ;
#define TG_ADD_BOOL_TRAIT(trait, type, val) \
template <> \
struct trait##_t<type> \
{ \
static constexpr bool value = val; \
}
} // enforce ;
#define TG_ADD_TYPE_TRAIT(trait, ttype, result_type) \
template <> \
struct trait##_t<ttype> \
{ \
using type = result_type; \
} // enforce ;
#define TG_INHERIT_TRAITS_D(ttype) \
template <int D, class ScalarT> \
struct is_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_signed_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_signed_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_unsigned_integer_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_unsigned_integer<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct is_floating_point_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = is_floating_point<ScalarT>; \
}; \
template <int D, class ScalarT> \
struct fractional_result_t<ttype<D, ScalarT>> \
{ \
using type = ttype<D, fractional_result<ScalarT>>; \
} // enforce ;
#define TG_INHERIT_TRAITS_FIELD_D(ttype) \
TG_INHERIT_TRAITS_D(ttype); \
template <int D, class ScalarT> \
struct has_multiplication_t<ttype<D, ScalarT>> \
{ \
static constexpr bool value = has_multiplication<ScalarT>; \
}; // enforce ;
// shape traits
TG_DEFINE_BOOL_TRAIT
(
is_scalar
,
false
);
...
...
@@ -31,6 +82,7 @@ TG_DEFINE_BOOL_TRAIT(is_integer, false);
TG_DEFINE_BOOL_TRAIT
(
is_floating_point
,
false
);
TG_DEFINE_BOOL_TRAIT
(
is_signed_integer
,
false
);
TG_DEFINE_BOOL_TRAIT
(
is_unsigned_integer
,
false
);
TG_DEFINE_BOOL_TRAIT
(
has_multiplication
,
true
);
// scalars
TG_ADD_BOOL_TRAIT
(
is_scalar
,
i8
,
true
);
...
...
@@ -73,33 +125,26 @@ TG_ADD_BOOL_TRAIT(is_floating_point, f16, true);
TG_ADD_BOOL_TRAIT
(
is_floating_point
,
f32
,
true
);
TG_ADD_BOOL_TRAIT
(
is_floating_point
,
f64
,
true
);
TG_ADD_BOOL_TRAIT
(
has_multiplication
,
bool
,
false
);
// type mapping
TG_DEFINE_TYPE_TRAIT
(
fractional_result
,
float
);
TG_ADD_TYPE_TRAIT
(
fractional_result
,
f64
,
f64
);
TG_ADD_TYPE_TRAIT
(
fractional_result
,
i64
,
f64
);
TG_ADD_TYPE_TRAIT
(
fractional_result
,
u64
,
f64
);
// vectors
template
<
int
D
,
class
ScalarT
>
struct
is_vector_t
<
vec
<
D
,
ScalarT
>>
{
static
constexpr
bool
value
=
true
;
};
template
<
int
D
,
class
ScalarT
>
struct
is_integer_t
<
vec
<
D
,
ScalarT
>>
{
static
constexpr
bool
value
=
is_integer
<
ScalarT
>
;
};
template
<
int
D
,
class
ScalarT
>
struct
is_signed_integer_t
<
vec
<
D
,
ScalarT
>>
{
static
constexpr
bool
value
=
is_signed_integer
<
ScalarT
>
;
};
template
<
int
D
,
class
ScalarT
>
struct
is_unsigned_integer_t
<
vec
<
D
,
ScalarT
>>
{
static
constexpr
bool
value
=
is_unsigned_integer
<
ScalarT
>
;
};
template
<
int
D
,
class
ScalarT
>
struct
is_floating_point_t
<
vec
<
D
,
ScalarT
>>
{
static
constexpr
bool
value
=
is_floating_point
<
ScalarT
>
;
};
TG_INHERIT_TRAITS_D
(
vec
);
#undef TG_DEFINE_BOOL_TRAIT
#undef TG_DEFINE_TYPE_TRAIT
#undef TG_ADD_BOOL_TRAIT
#undef TG_ADD_TYPE_TRAIT
#undef TG_INHERIT_TRAITS_D
#undef TG_INHERIT_TRAITS_FIELDS_D
}
// namespace tg
src/tg/detail/types.hh
deleted
100644 → 0
View file @
770c41fe
#pragma once
#include
<cstdint>
#include
"half.hh"
#include
"quarter.hh"
#include
"vec.hh"
namespace
tg
{
// Scalar types
using
i8
=
std
::
int8_t
;
using
i16
=
std
::
int16_t
;
using
i32
=
std
::
int32_t
;
using
i64
=
std
::
int64_t
;
using
u8
=
std
::
uint8_t
;
using
u16
=
std
::
uint16_t
;
using
u32
=
std
::
uint32_t
;
using
u64
=
std
::
uint64_t
;
using
f8
=
quarter
;
using
f16
=
half
;
using
f32
=
float
;
using
f64
=
double
;
// Common vector types
using
vec1
=
vec
<
1
,
f32
>
;
using
vec2
=
vec
<
2
,
f32
>
;
using
vec3
=
vec
<
3
,
f32
>
;
using
vec4
=
vec
<
4
,
f32
>
;
using
bvec1
=
vec
<
1
,
bool
>
;
using
bvec2
=
vec
<
2
,
bool
>
;
using
bvec3
=
vec
<
3
,
bool
>
;
using
bvec4
=
vec
<
4
,
bool
>
;
using
fvec1
=
vec
<
1
,
f32
>
;
using
fvec2
=
vec
<
2
,
f32
>
;
using
fvec3
=
vec
<
3
,
f32
>
;
using
fvec4
=
vec
<
4
,
f32
>
;
using
dvec1
=
vec
<
1
,
f64
>
;
using
dvec2
=
vec
<
2
,
f64
>
;
using
dvec3
=
vec
<
3
,
f64
>
;
using
dvec4
=
vec
<
4
,
f64
>
;
using
ivec1
=
vec
<
1
,
i32
>
;
using
ivec2
=
vec
<
2
,
i32
>
;
using
ivec3
=
vec
<
3
,
i32
>
;
using
ivec4
=
vec
<
4
,
i32
>
;
using
uvec1
=
vec
<
1
,
u32
>
;
using
uvec2
=
vec
<
2
,
u32
>
;
using
uvec3
=
vec
<
3
,
u32
>
;
using
uvec4
=
vec
<
4
,
u32
>
;
// Sized vector types
using
i8vec1
=
vec
<
1
,
i8
>
;
using
i8vec2
=
vec
<
2
,
i8
>
;
using
i8vec3
=
vec
<
3
,
i8
>
;
using
i8vec4
=
vec
<
4
,
i8
>
;
using
i16vec1
=
vec
<
1
,
i16
>
;
using
i16vec2
=
vec
<
2
,
i16
>
;
using
i16vec3
=
vec
<
3
,
i16
>
;
using
i16vec4
=
vec
<
4
,
i16
>
;
using
i32vec1
=
vec
<
1
,
i32
>
;
using
i32vec2
=
vec
<
2
,
i32
>
;
using
i32vec3
=
vec
<
3
,
i32
>
;
using
i32vec4
=
vec
<
4
,
i32
>
;
using
i64vec1
=
vec
<
1
,
i64
>
;
using
i64vec2
=
vec
<
2
,
i64
>
;
using
i64vec3
=
vec
<
3
,
i64
>
;
using
i64vec4
=
vec
<
4
,
i64
>
;
using
u8vec1
=
vec
<
1
,
u8
>
;
using
u8vec2
=
vec
<
2
,
u8
>
;
using
u8vec3
=
vec
<
3
,
u8
>
;
using
u8vec4
=
vec
<
4
,
u8
>
;
using
u16vec1
=
vec
<
1
,
u16
>
;
using
u16vec2
=
vec
<
2
,
u16
>
;
using
u16vec3
=
vec
<
3
,
u16
>
;
using
u16vec4
=
vec
<
4
,
u16
>
;
using
u32vec1
=
vec
<
1
,
u32
>
;
using
u32vec2
=
vec
<
2
,
u32
>
;
using
u32vec3
=
vec
<
3
,
u32
>
;
using
u32vec4
=
vec
<
4
,
u32
>
;
using
u64vec1
=
vec
<
1
,
u64
>
;
using
u64vec2
=
vec
<
2
,
u64
>
;
using
u64vec3
=
vec
<
3
,
u64
>
;
using
u64vec4
=
vec
<
4
,
u64
>
;
using
f8vec1
=
vec
<
1
,
f8
>
;
using
f8vec2
=
vec
<
2
,
f8
>
;
using
f8vec3
=
vec
<
3
,
f8
>
;
using
f8vec4
=
vec
<
4
,
f8
>
;
using
f16vec1
=
vec
<
1
,
f16
>
;
using
f16vec2
=
vec
<
2
,
f16
>
;
using
f16vec3
=
vec
<
3
,
f16
>
;
using
f16vec4
=
vec
<
4
,
f16
>
;
using
f32vec1
=
vec
<
1
,
f32
>
;
using
f32vec2
=
vec
<
2
,
f32
>
;
using
f32vec3
=
vec
<
3
,
f32
>
;
using
f32vec4
=
vec
<
4
,
f32
>
;
using
f64vec1
=
vec
<
1
,
f64
>
;
using
f64vec2
=
vec
<
2
,
f64
>
;
using
f64vec3
=
vec
<
3
,
f64
>
;
using
f64vec4
=
vec
<
4
,
f64
>
;
// Quaternion types
// TODO
// Matrix types
// TODO
}
// namespace tg
src/tg/detail/vec_ops.hh
View file @
a61b80fc
#pragma once
#include
"vec.hh"
#include
"op_helper.hh"
#include
"../types/vec.hh"
namespace
tg
{
...
...
@@ -15,14 +17,136 @@ constexpr ScalarT* data_ptr(vec<D, ScalarT>& v)
return
&
v
.
x
;
}
// -- operator@ --
template
<
int
D
,
class
ScalarT
>
constexpr
vec
<
D
,
ScalarT
>
operator
+
(
vec
<
D
,
ScalarT
>
const
&
a
,
vec
<
D
,
ScalarT
>
const
&
b
)
{
return
detail
::
compwise
<
ScalarT
>
(
a
,
b
,
detail
::
add
<
ScalarT
>
);
}
template
<
int
D
,
class
ScalarT
>
constexpr
vec
<
D
,
ScalarT
>
operator
+
(
vec
<
D
,
ScalarT
>
const
&
a
,
ScalarT
b
)
{
return
detail
::
compwise
<
ScalarT
>
(
a
,
b
,
detail
::
add
<
ScalarT
>
);
}
template
<
int
D
,
class
ScalarT
>
constexpr
vec
<
D
,
ScalarT
>
operator
-
(
vec
<
D
,
ScalarT
>
const
&
a
,
vec
<
D
,
ScalarT
>
const
&
b
)
{
return
detail
::
compwise
<
ScalarT
>
(
a
,
b
,
detail
::
sub
<
ScalarT
>
);
}
template
<
int
D
,
class
ScalarT
>
constexpr
vec
<
D
,
ScalarT
>
operator
-
(
vec
<
D
,
ScalarT
>
const
&
a
,
ScalarT
b
)
{
return
detail
::
compwise
<
ScalarT
&