Animation: Weight Paint select more/less for vertices #105633

Merged
Christoph Lendenfeld merged 14 commits from ChrisLend/blender:weight_paint_grow_sel_vert into main 2023-03-31 14:48:09 +02:00
19 changed files with 855 additions and 749 deletions
Showing only changes of commit 1eee63eb89 - Show all commits

View File

@ -366,24 +366,22 @@ int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
int fcurve_result_len); int fcurve_result_len);
/** /**
* Calculate the extents of F-Curve's keyframes. * Calculate the x range of the given F-Curve's data.
* \return true if a range has been found.
*/ */
bool BKE_fcurve_calc_range( bool BKE_fcurve_calc_range(const struct FCurve *fcu, float *r_min, float *r_max, bool selected_keys_only);
const struct FCurve *fcu, float *min, float *max, bool do_sel_only, bool do_min_length);
/** /**
* Calculate the extents of F-Curve's data. * Calculate the x and y extents of F-Curve's data.
* \param range Only calculate the bounds of the FCurve in the given range. * \param frame_range Only calculate the bounds of the FCurve in the given range.
* Does the full range if NULL. * Does the full range if NULL.
* \return true if the bounds have been found.
*/ */
bool BKE_fcurve_calc_bounds(const struct FCurve *fcu, bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
float *xmin, bool selected_keys_only,
float *xmax,
float *ymin,
float *ymax,
bool do_sel_only,
bool include_handles, bool include_handles,
const float range[2]); const float frame_range[2],
struct rctf *r_bounds);
/** /**
* Return an array of keyed frames, rounded to `interval`. * Return an array of keyed frames, rounded to `interval`.

View File

@ -1408,7 +1408,7 @@ void calc_action_range(const bAction *act, float *start, float *end, short incl_
* single-keyframe curves will increase the overall length by * single-keyframe curves will increase the overall length by
* a phantom frame (#50354) * a phantom frame (#50354)
*/ */
BKE_fcurve_calc_range(fcu, &nmin, &nmax, false, false); BKE_fcurve_calc_range(fcu, &nmin, &nmax, false);
/* compare to the running tally */ /* compare to the running tally */
min = min_ff(min, nmin); min = min_ff(min, nmin);

View File

@ -556,252 +556,236 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
/* ...................................... */ /* ...................................... */
/* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */ /* Get the first and last index to the bezt array that satisfies the given parameters.
static bool get_fcurve_end_keyframes(const FCurve *fcu, * \param selected_keys_only Only accept indices of bezt that are selected. Is a subset of
BezTriple **first, * frame_range. \param frame_range Only consider keyframes in that frame interval. Can be NULL.
BezTriple **last, */
const bool do_sel_only, static bool get_bounding_bezt_indices(const FCurve *fcu,
const float range[2]) const bool selected_keys_only,
const float frame_range[2],
int *r_first,
int *r_last)
{ {
bool found = false;
/* Init outputs. */
*first = NULL;
*last = NULL;
/* Sanity checks. */ /* Sanity checks. */
if (fcu->bezt == NULL) { if (fcu->bezt == NULL) {
return found; return false;
} }
int first_index = 0; *r_first = 0;
int last_index = fcu->totvert - 1; *r_last = fcu->totvert - 1;
if (range != NULL) { bool found = false;
if (frame_range != NULL) {
/* If a range is passed in find the first and last keyframe within that range. */ /* If a range is passed in find the first and last keyframe within that range. */
bool replace = false; bool replace = false;
first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace); *r_first = BKE_fcurve_bezt_binarysearch_index(
last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace); fcu->bezt, frame_range[0], fcu->totvert, &replace);
*r_last = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, frame_range[1], fcu->totvert, &replace);
/* If first and last index are the same, no keyframes were found in the range. */ /* If first and last index are the same, no keyframes were found in the range. */
if (first_index == last_index) { if (*r_first == *r_last) {
return found; return false;
} }
/* The binary search returns an index where a keyframe would be inserted, /* The binary search returns an index where a keyframe would be inserted,
* so it needs to be clamped to ensure it is in range of the array. */ * so it needs to be clamped to ensure it is in range of the array. */
first_index = clamp_i(first_index, 0, fcu->totvert - 1); *r_first = clamp_i(*r_first, 0, fcu->totvert - 1);
last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1); *r_last = clamp_i(*r_last - 1, 0, fcu->totvert - 1);
} }
/* Only include selected items? */ /* Only include selected items? */
if (do_sel_only) { if (selected_keys_only) {
/* Find first selected. */ /* Find first selected. */
for (int i = first_index; i <= last_index; i++) { for (int i = *r_first; i <= *r_last; i++) {
BezTriple *bezt = &fcu->bezt[i]; BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) { if (BEZT_ISSEL_ANY(bezt)) {
*first = bezt; *r_first = i;
found = true; found = true;
break; break;
} }
} }
/* Find last selected. */ /* Find last selected. */
for (int i = last_index; i >= first_index; i--) { for (int i = *r_last; i >= *r_first; i--) {
BezTriple *bezt = &fcu->bezt[i]; BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) { if (BEZT_ISSEL_ANY(bezt)) {
*last = bezt; *r_last = i;
found = true; found = true;
break; break;
} }
} }
} }
else { else {
*first = &fcu->bezt[first_index];
*last = &fcu->bezt[last_index];
found = true; found = true;
} }
return found; return found;
} }
bool BKE_fcurve_calc_bounds(const FCurve *fcu, static void calculate_bezt_bounds_x(BezTriple *bezt_array,
float *xmin, const int index_range[2],
float *xmax, const bool include_handles,
float *ymin, float *r_min,
float *ymax, float *r_max)
const bool do_sel_only,
const bool include_handles,
const float range[2])
{ {
float xminv = 999999999.0f, xmaxv = -999999999.0f; *r_min = bezt_array[index_range[0]].vec[1][0];
float yminv = 999999999.0f, ymaxv = -999999999.0f; *r_max = bezt_array[index_range[1]].vec[1][0];
bool foundvert = false;
const bool use_range = range != NULL; if (include_handles) {
/* Need to check all handles because they might extend beyond their neighboring keys. */
if (fcu->totvert) { for (int i = index_range[0]; i <= index_range[1]; i++) {
if (fcu->bezt) { const BezTriple *bezt = &bezt_array[i];
*r_min = min_fff(*r_min, bezt->vec[0][0], bezt->vec[1][0]);
if (xmin || xmax) { *r_max = max_fff(*r_max, bezt->vec[1][0], bezt->vec[2][0]);
BezTriple *bezt_first = NULL, *bezt_last = NULL;
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range);
if (bezt_first) {
BLI_assert(bezt_last != NULL);
foundvert = true;
if (include_handles) {
xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]);
xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]);
}
else {
xminv = min_ff(xminv, bezt_first->vec[1][0]);
xmaxv = max_ff(xmaxv, bezt_last->vec[1][0]);
}
}
}
/* Only loop over keyframes to find extents for values if needed. */
if (ymin || ymax) {
BezTriple *bezt, *prevbezt = NULL;
int i;
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) {
if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) {
continue;
}
if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) {
/* Keyframe itself. */
yminv = min_ff(yminv, bezt->vec[1][1]);
ymaxv = max_ff(ymaxv, bezt->vec[1][1]);
if (include_handles) {
/* Left handle - only if applicable.
* NOTE: for the very first keyframe,
* the left handle actually has no bearings on anything. */
if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) {
yminv = min_ff(yminv, bezt->vec[0][1]);
ymaxv = max_ff(ymaxv, bezt->vec[0][1]);
}
/* Right handle - only if applicable. */
if (bezt->ipo == BEZT_IPO_BEZ) {
yminv = min_ff(yminv, bezt->vec[2][1]);
ymaxv = max_ff(ymaxv, bezt->vec[2][1]);
}
}
foundvert = true;
}
}
}
}
else if (fcu->fpt) {
/* Frame range can be directly calculated from end verts. */
if (xmin || xmax) {
xminv = min_ff(xminv, fcu->fpt[0].vec[0]);
xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]);
}
/* Only loop over keyframes to find extents for values if needed. */
if (ymin || ymax) {
int i = 0;
for (FPoint *fpt = fcu->fpt; i < fcu->totvert; fpt++, i++) {
if (fpt->vec[1] < yminv) {
yminv = fpt->vec[1];
}
if (fpt->vec[1] > ymaxv) {
ymaxv = fpt->vec[1];
}
foundvert = true;
}
}
} }
} }
if (foundvert) {
if (xmin) {
*xmin = xminv;
}
if (xmax) {
*xmax = xmaxv;
}
if (ymin) {
*ymin = yminv;
}
if (ymax) {
*ymax = ymaxv;
}
}
else {
if (G.debug & G_DEBUG) {
printf("F-Curve calc bounds didn't find anything, so assuming minimum bounds of 1.0\n");
}
if (xmin) {
*xmin = use_range ? range[0] : 0.0f;
}
if (xmax) {
*xmax = use_range ? range[1] : 1.0f;
}
if (ymin) {
*ymin = 0.0f;
}
if (ymax) {
*ymax = 1.0f;
}
}
return foundvert;
} }
bool BKE_fcurve_calc_range( static void calculate_bezt_bounds_y(BezTriple *bezt_array,
const FCurve *fcu, float *start, float *end, const bool do_sel_only, const bool do_min_length) const int index_range[2],
const bool selected_keys_only,
const bool include_handles,
float *r_min,
float *r_max)
{ {
float min = 999999999.0f, max = -999999999.0f; *r_min = bezt_array[index_range[0]].vec[1][1];
*r_max = bezt_array[index_range[0]].vec[1][1];
for (int i = index_range[0]; i <= index_range[1]; i++) {
const BezTriple *bezt = &bezt_array[i];
if (selected_keys_only && !BEZT_ISSEL_ANY(bezt)) {
continue;
}
*r_min = min_ff(*r_min, bezt->vec[1][1]);
*r_max = max_ff(*r_max, bezt->vec[1][1]);
if (include_handles) {
*r_min = min_fff(*r_min, bezt->vec[0][1], bezt->vec[2][1]);
*r_max = max_fff(*r_max, bezt->vec[0][1], bezt->vec[2][1]);
}
}
}
static bool calculate_bezt_bounds(const FCurve *fcu,
const bool selected_keys_only,
const bool include_handles,
const float frame_range[2],
rctf *r_bounds)
{
int index_range[2];
const bool found_indices = get_bounding_bezt_indices(
fcu, selected_keys_only, frame_range, &index_range[0], &index_range[1]);
if (!found_indices) {
return false;
}
calculate_bezt_bounds_x(
fcu->bezt, index_range, include_handles, &r_bounds->xmin, &r_bounds->xmax);
calculate_bezt_bounds_y(fcu->bezt,
index_range,
selected_keys_only,
include_handles,
&r_bounds->ymin,
&r_bounds->ymax);
return true;
}
static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], rctf *r_bounds)
{
r_bounds->xmin = INFINITY;
r_bounds->xmax = -INFINITY;
r_bounds->ymin = INFINITY;
r_bounds->ymax = -INFINITY;
const int first_index = 0;
const int last_index = fcu->totvert - 1;
int start_index = first_index;
int end_index = last_index;
if (frame_range != NULL) {
/* Start index can be calculated because fpt has a key on every full frame. */
const float start_index_f = frame_range[0] - fcu->fpt[0].vec[0];
const float end_index_f = start_index_f + frame_range[1] - frame_range[0];
if (start_index_f > fcu->totvert - 1 || end_index_f < 0) {
/* Range is outside of keyframe samples. */
return false;
}
/* Range might be partially covering keyframe samples. */
start_index = clamp_i(start_index_f, 0, fcu->totvert - 1);
end_index = clamp_i(end_index_f, 0, fcu->totvert - 1);
}
/* X range can be directly calculated from end verts. */
r_bounds->xmin = fcu->fpt[start_index].vec[0];
r_bounds->xmax = fcu->fpt[end_index].vec[0];
for (int i = start_index; i <= end_index; i++) {
r_bounds->ymin = min_ff(r_bounds->ymin, fcu->fpt[i].vec[1]);
r_bounds->ymax = max_ff(r_bounds->ymax, fcu->fpt[i].vec[1]);
}
return BLI_rctf_is_valid(r_bounds);
}
bool BKE_fcurve_calc_bounds(const FCurve *fcu,
const bool selected_keys_only,
const bool include_handles,
const float frame_range[2],
rctf *r_bounds)
{
if (fcu->totvert == 0) {
return false;
}
if (fcu->bezt) {
const bool found_bounds = calculate_bezt_bounds(
fcu, selected_keys_only, include_handles, frame_range, r_bounds);
return found_bounds;
}
if (fcu->fpt) {
const bool founds_bounds = calculate_fpt_bounds(fcu, frame_range, r_bounds);
return founds_bounds;
}
return false;
}
bool BKE_fcurve_calc_range(const FCurve *fcu,
float *r_start,
float *r_end,
const bool selected_keys_only)
{
float min, max = 0.0f;
bool foundvert = false; bool foundvert = false;
if (fcu->totvert) { if (fcu->totvert == 0) {
if (fcu->bezt) { return false;
BezTriple *bezt_first = NULL, *bezt_last = NULL;
/* Get endpoint keyframes. */
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL);
if (bezt_first) {
BLI_assert(bezt_last != NULL);
min = min_ff(min, bezt_first->vec[1][0]);
max = max_ff(max, bezt_last->vec[1][0]);
foundvert = true;
}
}
else if (fcu->fpt) {
min = min_ff(min, fcu->fpt[0].vec[0]);
max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]);
foundvert = true;
}
} }
if (foundvert == false) { if (fcu->bezt) {
min = max = 0.0f; int index_range[2];
} foundvert = get_bounding_bezt_indices(
fcu, selected_keys_only, NULL, &index_range[0], &index_range[1]);
if (do_min_length) { if (!foundvert) {
/* Minimum length is 1 frame. */ return false;
if (min == max) {
max += 1.0f;
} }
const bool include_handles = false;
calculate_bezt_bounds_x(fcu->bezt, index_range, include_handles, &min, &max);
}
else if (fcu->fpt) {
min = fcu->fpt[0].vec[0];
max = fcu->fpt[fcu->totvert - 1].vec[0];
foundvert = true;
} }
*start = min; *r_start = min;
*end = max; *r_end = max;
return foundvert; return foundvert;
} }

View File

@ -346,4 +346,189 @@ TEST(BKE_fcurve, BKE_fcurve_keyframe_move_value_with_handles)
BKE_fcurve_free(fcu); BKE_fcurve_free(fcu);
} }
TEST(BKE_fcurve, BKE_fcurve_calc_range)
{
FCurve *fcu = BKE_fcurve_create();
insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 4.0f, -15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 18.2f, -20.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
for (int i = 0; i < fcu->totvert; i++) {
fcu->bezt[i].f1 &= ~SELECT;
fcu->bezt[i].f2 &= ~SELECT;
fcu->bezt[i].f3 &= ~SELECT;
}
float min, max;
bool success;
/* All keys. */
success = BKE_fcurve_calc_range(fcu, &min, &max, false);
EXPECT_TRUE(success) << "A non-empty FCurve should have a range.";
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], min);
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][0], max);
/* Only selected. */
success = BKE_fcurve_calc_range(fcu, &min, &max, true);
EXPECT_FALSE(success)
<< "Using selected keyframes only should not find a range if nothing is selected.";
fcu->bezt[1].f2 |= SELECT;
fcu->bezt[3].f2 |= SELECT;
success = BKE_fcurve_calc_range(fcu, &min, &max, true);
EXPECT_TRUE(success) << "Range of selected keyframes should have been found.";
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], min);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], max);
/* Curve samples. */
const int sample_start = 1;
const int sample_end = 20;
fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve);
success = BKE_fcurve_calc_range(fcu, &min, &max, true);
EXPECT_TRUE(success) << "FCurve samples should have a range.";
EXPECT_FLOAT_EQ(sample_start, min);
EXPECT_FLOAT_EQ(sample_end, max);
BKE_fcurve_free(fcu);
}
TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
{
FCurve *fcu = BKE_fcurve_create();
insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 4.0f, -15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
insert_vert_fcurve(fcu, 18.2f, -20.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
for (int i = 0; i < fcu->totvert; i++) {
fcu->bezt[i].f1 &= ~SELECT;
fcu->bezt[i].f2 &= ~SELECT;
fcu->bezt[i].f3 &= ~SELECT;
}
fcu->bezt[0].vec[0][0] = -5.0f;
fcu->bezt[4].vec[2][0] = 25.0f;
rctf bounds;
bool success;
/* All keys. */
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
EXPECT_TRUE(success) << "A non-empty FCurve should have bounds.";
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax);
/* Only selected. */
success = BKE_fcurve_calc_bounds(
fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
EXPECT_FALSE(success)
<< "Using selected keyframes only should not find bounds if nothing is selected.";
fcu->bezt[1].f2 |= SELECT;
fcu->bezt[3].f2 |= SELECT;
success = BKE_fcurve_calc_bounds(
fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
EXPECT_TRUE(success) << "Selected keys should have been found.";
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][1], bounds.ymax);
/* Including handles. */
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, true /* include handles */, NULL /* frame range */, &bounds);
EXPECT_TRUE(success) << "A non-empty FCurve should have bounds including handles.";
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[2][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax);
/* Range. */
float range[2];
range[0] = 25;
range[1] = 30;
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
EXPECT_FALSE(success) << "A frame range outside the range of keyframes should not find bounds.";
range[0] = 0;
range[1] = 18.2f;
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
EXPECT_TRUE(success) << "A frame range within the range of keyframes should find bounds.";
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax);
/* Range and handles. */
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, true /* include handles */, range /* frame range */, &bounds);
EXPECT_TRUE(success)
<< "A frame range within the range of keyframes should find bounds with handles.";
EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax);
/* Range, handles and only selection. */
range[0] = 8.0f;
range[1] = 18.2f;
success = BKE_fcurve_calc_bounds(
fcu, true /* sel only */, true /* include handles */, range /* frame range */, &bounds);
EXPECT_TRUE(success)
<< "A frame range within the range of keyframes should find bounds of selected keyframes.";
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[0][0], bounds.xmin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][0], bounds.xmax);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][1], bounds.ymin);
EXPECT_FLOAT_EQ(fcu->bezt[3].vec[0][1], bounds.ymax);
/* Curve samples. */
const int sample_start = 1;
const int sample_end = 20;
fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve);
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds);
EXPECT_TRUE(success) << "FCurve samples should have a range.";
EXPECT_FLOAT_EQ(sample_start, bounds.xmin);
EXPECT_FLOAT_EQ(sample_end, bounds.xmax);
EXPECT_FLOAT_EQ(-20.0f, bounds.ymin);
EXPECT_FLOAT_EQ(15.0f, bounds.ymax);
range[0] = 8.0f;
range[1] = 20.0f;
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
EXPECT_TRUE(success) << "FCurve samples should have a range.";
EXPECT_FLOAT_EQ(range[0], bounds.xmin);
EXPECT_FLOAT_EQ(range[1], bounds.xmax);
EXPECT_FLOAT_EQ(-20.0f, bounds.ymin);
EXPECT_FLOAT_EQ(15.0f, bounds.ymax);
range[0] = 20.1f;
range[1] = 30.0f;
success = BKE_fcurve_calc_bounds(
fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds);
EXPECT_FALSE(success)
<< "A frame range outside the range of keyframe samples should not have bounds.";
BKE_fcurve_free(fcu);
}
} // namespace blender::bke::tests } // namespace blender::bke::tests

View File

@ -19,33 +19,31 @@
namespace blender::math { namespace blender::math {
namespace detail {
/** /**
* A `blender::math::AngleRadian<T>` is a typical radian angle. * A `blender::math::AngleRadianBase<T>` is a typical radian angle.
* - Storage : `1 * sizeof(T)` * - Storage : `1 * sizeof(T)`
* - Range : [-inf..inf] * - Range : [-inf..inf]
* - Fast : Everything not slow. * - Fast : Everything not slow.
* - Slow : `cos()`, `sin()`, `tan()`, `AngleRadian(cos, sin)` * - Slow : `cos()`, `sin()`, `tan()`, `AngleRadian(cos, sin)`
*/ */
template<typename T> struct AngleRadian { template<typename T> struct AngleRadianBase {
private: private:
T value_; T value_;
public: public:
AngleRadian() = default; AngleRadianBase() = default;
AngleRadian(const T &radian) : value_(radian){}; AngleRadianBase(const T &radian) : value_(radian){};
explicit AngleRadian(const T &cos, const T &sin) : value_(math::atan2(sin, cos)){}; explicit AngleRadianBase(const T &cos, const T &sin) : value_(math::atan2(sin, cos)){};
/** Static functions. */ /** Static functions. */
static AngleRadian identity() static AngleRadianBase identity()
{ {
return 0; return 0;
} }
static AngleRadian from_degree(const T &degrees) static AngleRadianBase from_degree(const T &degrees)
{ {
return degrees * T(M_PI / 180.0); return degrees * T(M_PI / 180.0);
} }
@ -81,7 +79,7 @@ template<typename T> struct AngleRadian {
/** /**
* Return the angle wrapped inside [-pi..pi] interval. Basically `(angle + pi) % 2pi - pi`. * Return the angle wrapped inside [-pi..pi] interval. Basically `(angle + pi) % 2pi - pi`.
*/ */
AngleRadian wrapped() const AngleRadianBase wrapped() const
{ {
return math::mod_periodic(value_ + T(M_PI), T(2 * M_PI)) - T(M_PI); return math::mod_periodic(value_ + T(M_PI), T(2 * M_PI)) - T(M_PI);
} }
@ -92,80 +90,80 @@ template<typename T> struct AngleRadian {
* This means the interpolation between the returned value and \a reference will always take the * This means the interpolation between the returned value and \a reference will always take the
* shortest path. * shortest path.
*/ */
AngleRadian wrapped_around(const AngleRadian &reference) const AngleRadianBase wrapped_around(const AngleRadianBase &reference) const
{ {
return reference + (*this - reference).wrapped(); return reference + (*this - reference).wrapped();
} }
/** Operators. */ /** Operators. */
friend AngleRadian operator+(const AngleRadian &a, const AngleRadian &b) friend AngleRadianBase operator+(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return a.value_ + b.value_; return a.value_ + b.value_;
} }
friend AngleRadian operator-(const AngleRadian &a, const AngleRadian &b) friend AngleRadianBase operator-(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return a.value_ - b.value_; return a.value_ - b.value_;
} }
friend AngleRadian operator*(const AngleRadian &a, const AngleRadian &b) friend AngleRadianBase operator*(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return a.value_ * b.value_; return a.value_ * b.value_;
} }
friend AngleRadian operator/(const AngleRadian &a, const AngleRadian &b) friend AngleRadianBase operator/(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return a.value_ / b.value_; return a.value_ / b.value_;
} }
friend AngleRadian operator-(const AngleRadian &a) friend AngleRadianBase operator-(const AngleRadianBase &a)
{ {
return -a.value_; return -a.value_;
} }
AngleRadian &operator+=(const AngleRadian &b) AngleRadianBase &operator+=(const AngleRadianBase &b)
{ {
value_ += b.value_; value_ += b.value_;
return *this; return *this;
} }
AngleRadian &operator-=(const AngleRadian &b) AngleRadianBase &operator-=(const AngleRadianBase &b)
{ {
value_ -= b.value_; value_ -= b.value_;
return *this; return *this;
} }
AngleRadian &operator*=(const AngleRadian &b) AngleRadianBase &operator*=(const AngleRadianBase &b)
{ {
value_ *= b.value_; value_ *= b.value_;
return *this; return *this;
} }
AngleRadian &operator/=(const AngleRadian &b) AngleRadianBase &operator/=(const AngleRadianBase &b)
{ {
value_ /= b.value_; value_ /= b.value_;
return *this; return *this;
} }
friend bool operator==(const AngleRadian &a, const AngleRadian &b) friend bool operator==(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return a.value_ == b.value_; return a.value_ == b.value_;
} }
friend bool operator!=(const AngleRadian &a, const AngleRadian &b) friend bool operator!=(const AngleRadianBase &a, const AngleRadianBase &b)
{ {
return !(a == b); return !(a == b);
} }
friend std::ostream &operator<<(std::ostream &stream, const AngleRadian &rot) friend std::ostream &operator<<(std::ostream &stream, const AngleRadianBase &rot)
{ {
return stream << "AngleRadian(" << rot.value_ << ")"; return stream << "AngleRadian(" << rot.value_ << ")";
} }
}; };
/** /**
* A `blender::math::AngleCartesian<T>` stores the angle as cosine + sine tuple. * A `blender::math::AngleCartesianBase<T>` stores the angle as cosine + sine tuple.
* - Storage : `2 * sizeof(T)` * - Storage : `2 * sizeof(T)`
* - Range : [-pi..pi] * - Range : [-pi..pi]
* - Fast : `cos()`, `sin()`, `tan()`, `AngleCartesian(cos, sin)` * - Fast : `cos()`, `sin()`, `tan()`, `AngleCartesian(cos, sin)`
@ -175,18 +173,18 @@ template<typename T> struct AngleRadian {
* advantage when trigonometric values of an angle are required but not directly the angle itself. * advantage when trigonometric values of an angle are required but not directly the angle itself.
* It is also a nice shortcut for using the trigonometric identities. * It is also a nice shortcut for using the trigonometric identities.
*/ */
template<typename T> struct AngleCartesian { template<typename T> struct AngleCartesianBase {
private: private:
T cos_; T cos_;
T sin_; T sin_;
public: public:
AngleCartesian() = default; AngleCartesianBase() = default;
/** /**
* Create an angle from a (x, y) position on the unit circle. * Create an angle from a (x, y) position on the unit circle.
*/ */
AngleCartesian(const T &x, const T &y) : cos_(x), sin_(y) AngleCartesianBase(const T &x, const T &y) : cos_(x), sin_(y)
{ {
BLI_assert(math::abs(x * x + y * y - T(1)) < T(1e-4)); BLI_assert(math::abs(x * x + y * y - T(1)) < T(1e-4));
} }
@ -194,34 +192,34 @@ template<typename T> struct AngleCartesian {
/** /**
* Create an angle from a radian value. * Create an angle from a radian value.
*/ */
explicit AngleCartesian(const T &radian) explicit AngleCartesianBase(const T &radian)
: AngleCartesian(math::cos(radian), math::sin(radian)){}; : AngleCartesianBase(math::cos(radian), math::sin(radian)){};
explicit AngleCartesian(const AngleRadian<T> &angle) explicit AngleCartesianBase(const AngleRadianBase<T> &angle)
: AngleCartesian(math::cos(angle.radian()), math::sin(angle.radian())){}; : AngleCartesianBase(math::cos(angle.radian()), math::sin(angle.radian())){};
/** Static functions. */ /** Static functions. */
static AngleCartesian identity() static AngleCartesianBase identity()
{ {
return {1, 0}; return {1, 0};
} }
static AngleCartesian from_degree(const T &degrees) static AngleCartesianBase from_degree(const T &degrees)
{ {
return AngleCartesian(degrees * T(M_PI / 180.0)); return AngleCartesianBase(degrees * T(M_PI / 180.0));
} }
/** /**
* Create an angle from a (x, y) position on the 2D plane. * Create an angle from a (x, y) position on the 2D plane.
* Fallback to identity if (x, y) is origin (0, 0). * Fallback to identity if (x, y) is origin (0, 0).
*/ */
static AngleCartesian from_point(const T &x, const T &y) static AngleCartesianBase from_point(const T &x, const T &y)
{ {
T norm = math::sqrt(x * x + y * y); T norm = math::sqrt(x * x + y * y);
if (norm == 0) { if (norm == 0) {
return identity(); return identity();
} }
return AngleCartesian(x / norm, y / norm); return AngleCartesianBase(x / norm, y / norm);
} }
/** Conversions. */ /** Conversions. */
@ -273,17 +271,17 @@ template<typename T> struct AngleCartesian {
* purpose of this class). * purpose of this class).
*/ */
friend AngleCartesian operator+(const AngleCartesian &a, const AngleCartesian &b) friend AngleCartesianBase operator+(const AngleCartesianBase &a, const AngleCartesianBase &b)
{ {
return {a.cos_ * b.cos_ - a.sin_ * b.sin_, a.sin_ * b.cos_ + a.cos_ * b.sin_}; return {a.cos_ * b.cos_ - a.sin_ * b.sin_, a.sin_ * b.cos_ + a.cos_ * b.sin_};
} }
friend AngleCartesian operator-(const AngleCartesian &a, const AngleCartesian &b) friend AngleCartesianBase operator-(const AngleCartesianBase &a, const AngleCartesianBase &b)
{ {
return {a.cos_ * b.cos_ + a.sin_ * b.sin_, a.sin_ * b.cos_ - a.cos_ * b.sin_}; return {a.cos_ * b.cos_ + a.sin_ * b.sin_, a.sin_ * b.cos_ - a.cos_ * b.sin_};
} }
friend AngleCartesian operator*(const AngleCartesian &a, const T &b) friend AngleCartesianBase operator*(const AngleCartesianBase &a, const T &b)
{ {
if (b == T(2)) { if (b == T(2)) {
return {a.cos_ * a.cos_ - a.sin_ * a.sin_, T(2) * a.sin_ * a.cos_}; return {a.cos_ * a.cos_ - a.sin_ * a.sin_, T(2) * a.sin_ * a.cos_};
@ -293,24 +291,24 @@ template<typename T> struct AngleCartesian {
T(3) * a.sin_ - T(4) * (a.sin_ * a.sin_ * a.sin_)}; T(3) * a.sin_ - T(4) * (a.sin_ * a.sin_ * a.sin_)};
} }
BLI_assert_msg(0, BLI_assert_msg(0,
"Arbitrary angle product isn't supported with AngleCartesian<T> for " "Arbitrary angle product isn't supported with AngleCartesianBase<T> for "
"performance reason. Use AngleRadian<T> instead."); "performance reason. Use AngleRadianBase<T> instead.");
return identity(); return identity();
} }
friend AngleCartesian operator*(const T &b, const AngleCartesian &a) friend AngleCartesianBase operator*(const T &b, const AngleCartesianBase &a)
{ {
return a * b; return a * b;
} }
friend AngleCartesian operator/(const AngleCartesian &a, const T &divisor) friend AngleCartesianBase operator/(const AngleCartesianBase &a, const T &divisor)
{ {
if (divisor == T(2)) { if (divisor == T(2)) {
/* Still costly but faster than using `atan()`. */ /* Still costly but faster than using `atan()`. */
AngleCartesian result = {math::sqrt((T(1) + a.cos_) / T(2)), AngleCartesianBase result = {math::sqrt((T(1) + a.cos_) / T(2)),
math::sqrt((T(1) - a.cos_) / T(2))}; math::sqrt((T(1) - a.cos_) / T(2))};
/* Recover sign only for sine. Cosine of half angle is given to be positive or 0 since the /* Recover sign only for sine. Cosine of half angle is given to be positive or 0 since the
* angle stored in #AngleCartesian is in the range [-pi..pi]. */ * angle stored in #AngleCartesianBase is in the range [-pi..pi]. */
/* TODO(fclem): Could use copysign here. */ /* TODO(fclem): Could use copysign here. */
if (a.sin_ < T(0)) { if (a.sin_ < T(0)) {
result.sin_ = -result.sin_; result.sin_ = -result.sin_;
@ -318,58 +316,56 @@ template<typename T> struct AngleCartesian {
return result; return result;
} }
BLI_assert_msg(0, BLI_assert_msg(0,
"Arbitrary angle quotient isn't supported with AngleCartesian<T> for " "Arbitrary angle quotient isn't supported with AngleCartesianBase<T> for "
"performance reason. Use AngleRadian<T> instead."); "performance reason. Use AngleRadianBase<T> instead.");
return identity(); return identity();
} }
friend AngleCartesian operator-(const AngleCartesian &a) friend AngleCartesianBase operator-(const AngleCartesianBase &a)
{ {
return {a.cos_, -a.sin_}; return {a.cos_, -a.sin_};
} }
AngleCartesian &operator+=(const AngleCartesian &b) AngleCartesianBase &operator+=(const AngleCartesianBase &b)
{ {
*this = *this + b; *this = *this + b;
return *this; return *this;
} }
AngleCartesian &operator*=(const T &b) AngleCartesianBase &operator*=(const T &b)
{ {
*this = *this * b; *this = *this * b;
return *this; return *this;
} }
AngleCartesian &operator-=(const AngleCartesian &b) AngleCartesianBase &operator-=(const AngleCartesianBase &b)
{ {
*this = *this - b; *this = *this - b;
return *this; return *this;
} }
AngleCartesian &operator/=(const T &b) AngleCartesianBase &operator/=(const T &b)
{ {
*this = *this / b; *this = *this / b;
return *this; return *this;
} }
friend bool operator==(const AngleCartesian &a, const AngleCartesian &b) friend bool operator==(const AngleCartesianBase &a, const AngleCartesianBase &b)
{ {
return a.cos_ == b.cos_ && a.sin_ == b.sin_; return a.cos_ == b.cos_ && a.sin_ == b.sin_;
} }
friend bool operator!=(const AngleCartesian &a, const AngleCartesian &b) friend bool operator!=(const AngleCartesianBase &a, const AngleCartesianBase &b)
{ {
return !(a == b); return !(a == b);
} }
friend std::ostream &operator<<(std::ostream &stream, const AngleCartesian &rot) friend std::ostream &operator<<(std::ostream &stream, const AngleCartesianBase &rot)
{ {
return stream << "AngleCartesian(x=" << rot.cos_ << ", y=" << rot.sin_ << ")"; return stream << "AngleCartesian(x=" << rot.cos_ << ", y=" << rot.sin_ << ")";
} }
}; };
} // namespace detail
/** /**
* A `blender::math::AngleFraction<T>` stores a radian angle as quotient. * A `blender::math::AngleFraction<T>` stores a radian angle as quotient.
* - Storage : `2 * sizeof(int64_t)` * - Storage : `2 * sizeof(int64_t)`
@ -385,7 +381,7 @@ template<typename T> struct AngleCartesian {
* not as cheap as a `AngleRadian`. Another nice property is that the `cos()` and `sin()` functions * not as cheap as a `AngleRadian`. Another nice property is that the `cos()` and `sin()` functions
* give symmetric results around the circle. * give symmetric results around the circle.
* *
* NOTE: Prefer converting to `blender::math::AngleCartesian<T>` if both `cos()` and `sin()` * NOTE: Prefer converting to `blender::math::AngleCartesianBase<T>` if both `cos()` and `sin()`
* are needed. This will save some computation. * are needed. This will save some computation.
* *
* Any operation becomes undefined if either the numerator or the denominator overflows. * Any operation becomes undefined if either the numerator or the denominator overflows.
@ -611,7 +607,7 @@ template<typename T = float> struct AngleFraction {
<< ")"; << ")";
} }
operator detail::AngleCartesian<T>() const operator AngleCartesianBase<T>() const
{ {
AngleFraction a = this->wrapped(); AngleFraction a = this->wrapped();
BLI_assert(abs(a.numerator_) <= a.denominator_); BLI_assert(abs(a.numerator_) <= a.denominator_);
@ -698,51 +694,51 @@ template<typename T = float> struct AngleFraction {
if (is_negative) { if (is_negative) {
y = -y; y = -y;
} }
return detail::AngleCartesian<T>(x, y); return AngleCartesianBase<T>(x, y);
} }
}; };
template<typename T> T cos(const detail::AngleRadian<T> &a) template<typename T> T cos(const AngleRadianBase<T> &a)
{ {
return cos(a.radian()); return cos(a.radian());
} }
template<typename T> T sin(const detail::AngleRadian<T> &a) template<typename T> T sin(const AngleRadianBase<T> &a)
{ {
return sin(a.radian()); return sin(a.radian());
} }
template<typename T> T tan(const detail::AngleRadian<T> &a) template<typename T> T tan(const AngleRadianBase<T> &a)
{ {
return tan(a.radian()); return tan(a.radian());
} }
template<typename T> T cos(const detail::AngleCartesian<T> &a) template<typename T> T cos(const AngleCartesianBase<T> &a)
{ {
return a.cos(); return a.cos();
} }
template<typename T> T sin(const detail::AngleCartesian<T> &a) template<typename T> T sin(const AngleCartesianBase<T> &a)
{ {
return a.sin(); return a.sin();
} }
template<typename T> T tan(const detail::AngleCartesian<T> &a) template<typename T> T tan(const AngleCartesianBase<T> &a)
{ {
return a.tan(); return a.tan();
} }
template<typename T> T cos(const AngleFraction<T> &a) template<typename T> T cos(const AngleFraction<T> &a)
{ {
return cos(detail::AngleCartesian<T>(a)); return cos(AngleCartesianBase<T>(a));
} }
template<typename T> T sin(const AngleFraction<T> &a) template<typename T> T sin(const AngleFraction<T> &a)
{ {
return sin(detail::AngleCartesian<T>(a)); return sin(AngleCartesianBase<T>(a));
} }
template<typename T> T tan(const AngleFraction<T> &a) template<typename T> T tan(const AngleFraction<T> &a)
{ {
return tan(detail::AngleCartesian<T>(a)); return tan(AngleCartesianBase<T>(a));
} }
using AngleRadian = math::detail::AngleRadian<float>; using AngleRadian = AngleRadianBase<float>;
using AngleCartesian = math::detail::AngleCartesian<float>; using AngleCartesian = AngleCartesianBase<float>;
} // namespace blender::math } // namespace blender::math

View File

@ -13,14 +13,14 @@
#include "BLI_math_matrix.hh" #include "BLI_math_matrix.hh"
#include "BLI_math_quaternion.hh" #include "BLI_math_quaternion.hh"
namespace blender::math::detail { namespace blender::math {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Constructors /** \name Constructors
* \{ */ * \{ */
template<typename T, typename AngleT> template<typename T, typename AngleT>
AxisAngle<T, AngleT>::AxisAngle(const VecBase<T, 3> &axis, const AngleT &angle) AxisAngleBase<T, AngleT>::AxisAngleBase(const VecBase<T, 3> &axis, const AngleT &angle)
{ {
BLI_assert(is_unit_scale(axis)); BLI_assert(is_unit_scale(axis));
axis_ = axis; axis_ = axis;
@ -28,14 +28,14 @@ AxisAngle<T, AngleT>::AxisAngle(const VecBase<T, 3> &axis, const AngleT &angle)
} }
template<typename T, typename AngleT> template<typename T, typename AngleT>
AxisAngle<T, AngleT>::AxisAngle(const AxisSigned axis, const AngleT &angle) AxisAngleBase<T, AngleT>::AxisAngleBase(const AxisSigned axis, const AngleT &angle)
{ {
axis_ = to_vector<VecBase<T, 3>>(axis); axis_ = to_vector<VecBase<T, 3>>(axis);
angle_ = angle; angle_ = angle;
} }
template<typename T, typename AngleT> template<typename T, typename AngleT>
AxisAngle<T, AngleT>::AxisAngle(const VecBase<T, 3> &from, const VecBase<T, 3> &to) AxisAngleBase<T, AngleT>::AxisAngleBase(const VecBase<T, 3> &from, const VecBase<T, 3> &to)
{ {
BLI_assert(is_unit_scale(from)); BLI_assert(is_unit_scale(from));
BLI_assert(is_unit_scale(to)); BLI_assert(is_unit_scale(to));
@ -61,16 +61,12 @@ AxisAngle<T, AngleT>::AxisAngle(const VecBase<T, 3> &from, const VecBase<T, 3> &
/** \} */ /** \} */
} // namespace blender::math::detail
namespace blender::math {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Conversion to Quaternions /** \name Conversion to Quaternions
* \{ */ * \{ */
template<typename T, typename AngleT> template<typename T, typename AngleT>
detail::Quaternion<T> to_quaternion(const detail::AxisAngle<T, AngleT> &axis_angle) QuaternionBase<T> to_quaternion(const AxisAngleBase<T, AngleT> &axis_angle)
{ {
BLI_assert(math::is_unit_scale(axis_angle.axis())); BLI_assert(math::is_unit_scale(axis_angle.axis()));
@ -79,7 +75,7 @@ detail::Quaternion<T> to_quaternion(const detail::AxisAngle<T, AngleT> &axis_ang
T hc = math::cos(half_angle); T hc = math::cos(half_angle);
VecBase<T, 3> xyz = axis_angle.axis() * hs; VecBase<T, 3> xyz = axis_angle.axis() * hs;
return detail::Quaternion<T>(hc, xyz.x, xyz.y, xyz.z); return QuaternionBase<T>(hc, xyz.x, xyz.y, xyz.z);
} }
/** \} */ /** \} */
@ -89,25 +85,25 @@ detail::Quaternion<T> to_quaternion(const detail::AxisAngle<T, AngleT> &axis_ang
* \{ */ * \{ */
template<typename T, typename AngleT> template<typename T, typename AngleT>
detail::Euler3<T> to_euler(const detail::AxisAngle<T, AngleT> &axis_angle, EulerOrder order) Euler3Base<T> to_euler(const AxisAngleBase<T, AngleT> &axis_angle, EulerOrder order)
{ {
/* Use quaternions as intermediate representation for now... */ /* Use quaternions as intermediate representation for now... */
return to_euler(to_quaternion(axis_angle), order); return to_euler(to_quaternion(axis_angle), order);
} }
template<typename T, typename AngleT> template<typename T, typename AngleT>
detail::EulerXYZ<T> to_euler(const detail::AxisAngle<T, AngleT> &axis_angle) EulerXYZBase<T> to_euler(const AxisAngleBase<T, AngleT> &axis_angle)
{ {
/* Check easy and exact conversions first. */ /* Check easy and exact conversions first. */
const VecBase<T, 3> axis = axis_angle.axis(); const VecBase<T, 3> axis = axis_angle.axis();
if (axis.x == T(1)) { if (axis.x == T(1)) {
return detail::EulerXYZ<T>(T(axis_angle.angle()), T(0), T(0)); return EulerXYZBase<T>(T(axis_angle.angle()), T(0), T(0));
} }
else if (axis.y == T(1)) { else if (axis.y == T(1)) {
return detail::EulerXYZ<T>(T(0), T(axis_angle.angle()), T(0)); return EulerXYZBase<T>(T(0), T(axis_angle.angle()), T(0));
} }
else if (axis.z == T(1)) { else if (axis.z == T(1)) {
return detail::EulerXYZ<T>(T(0), T(0), T(axis_angle.angle())); return EulerXYZBase<T>(T(0), T(0), T(axis_angle.angle()));
} }
/* Use quaternions as intermediate representation for now... */ /* Use quaternions as intermediate representation for now... */
return to_euler(to_quaternion(axis_angle)); return to_euler(to_quaternion(axis_angle));

View File

@ -14,8 +14,8 @@
* 2D operations and are thus faster. * 2D operations and are thus faster.
* *
* Interpolation isn't possible between two `blender::math::AxisAngle<T>`; they must be * Interpolation isn't possible between two `blender::math::AxisAngle<T>`; they must be
* converted to other rotation types for that. Converting to `blender::math::Quaternion<T>` is the * converted to other rotation types for that. Converting to `blender::math::QuaternionBase<T>` is
* fastest and more correct option. * the fastest and more correct option.
*/ */
#include "BLI_math_angle_types.hh" #include "BLI_math_angle_types.hh"
@ -25,9 +25,7 @@
namespace blender::math { namespace blender::math {
namespace detail { template<typename T, typename AngleT> struct AxisAngleBase {
template<typename T, typename AngleT> struct AxisAngle {
using vec3_type = VecBase<T, 3>; using vec3_type = VecBase<T, 3>;
private: private:
@ -36,29 +34,29 @@ template<typename T, typename AngleT> struct AxisAngle {
AngleT angle_ = AngleT::identity(); AngleT angle_ = AngleT::identity();
public: public:
AxisAngle() = default; AxisAngleBase() = default;
/** /**
* Create a rotation from a basis axis and an angle. * Create a rotation from a basis axis and an angle.
*/ */
AxisAngle(const AxisSigned axis, const AngleT &angle); AxisAngleBase(const AxisSigned axis, const AngleT &angle);
/** /**
* Create a rotation from an axis and an angle. * Create a rotation from an axis and an angle.
* \note `axis` have to be normalized. * \note `axis` have to be normalized.
*/ */
AxisAngle(const vec3_type &axis, const AngleT &angle); AxisAngleBase(const vec3_type &axis, const AngleT &angle);
/** /**
* Create a rotation from 2 normalized vectors. * Create a rotation from 2 normalized vectors.
* \note `from` and `to` must be normalized. * \note `from` and `to` must be normalized.
* \note Consider using `AxisAngleCartesian` for faster conversion to other rotation. * \note Consider using `AxisAngleCartesian` for faster conversion to other rotation.
*/ */
AxisAngle(const vec3_type &from, const vec3_type &to); AxisAngleBase(const vec3_type &from, const vec3_type &to);
/** Static functions. */ /** Static functions. */
static AxisAngle identity() static AxisAngleBase identity()
{ {
return {}; return {};
} }
@ -77,26 +75,24 @@ template<typename T, typename AngleT> struct AxisAngle {
/** Operators. */ /** Operators. */
friend bool operator==(const AxisAngle &a, const AxisAngle &b) friend bool operator==(const AxisAngleBase &a, const AxisAngleBase &b)
{ {
return (a.axis() == b.axis()) && (a.angle() == b.angle()); return (a.axis() == b.axis()) && (a.angle() == b.angle());
} }
friend bool operator!=(const AxisAngle &a, const AxisAngle &b) friend bool operator!=(const AxisAngleBase &a, const AxisAngleBase &b)
{ {
return (a != b); return (a != b);
} }
friend std::ostream &operator<<(std::ostream &stream, const AxisAngle &rot) friend std::ostream &operator<<(std::ostream &stream, const AxisAngleBase &rot)
{ {
return stream << "AxisAngle(axis=" << rot.axis() << ", angle=" << rot.angle() << ")"; return stream << "AxisAngle(axis=" << rot.axis() << ", angle=" << rot.angle() << ")";
} }
}; };
}; // namespace detail using AxisAngle = AxisAngleBase<float, AngleRadianBase<float>>;
using AxisAngleCartesian = AxisAngleBase<float, AngleCartesianBase<float>>;
using AxisAngle = math::detail::AxisAngle<float, detail::AngleRadian<float>>;
using AxisAngleCartesian = math::detail::AxisAngle<float, detail::AngleCartesian<float>>;
} // namespace blender::math } // namespace blender::math

View File

@ -14,43 +14,40 @@
#include "BLI_math_matrix.hh" #include "BLI_math_matrix.hh"
#include "BLI_math_quaternion.hh" #include "BLI_math_quaternion.hh"
namespace blender::math::detail { namespace blender::math {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name EulerXYZ /** \name EulerXYZ
* \{ */ * \{ */
template<typename T> EulerXYZ<T> EulerXYZ<T>::wrapped() const template<typename T> EulerXYZBase<T> EulerXYZBase<T>::wrapped() const
{ {
EulerXYZ<T> result(*this); EulerXYZBase<T> result(*this);
result.x() = AngleRadian<T>(result.x()).wrapped().radian(); result.x() = AngleRadianBase<T>(result.x()).wrapped().radian();
result.y() = AngleRadian<T>(result.y()).wrapped().radian(); result.y() = AngleRadianBase<T>(result.y()).wrapped().radian();
result.z() = AngleRadian<T>(result.z()).wrapped().radian(); result.z() = AngleRadianBase<T>(result.z()).wrapped().radian();
return result; return result;
} }
template<typename T> EulerXYZ<T> EulerXYZ<T>::wrapped_around(const EulerXYZ &reference) const template<typename T>
EulerXYZBase<T> EulerXYZBase<T>::wrapped_around(const EulerXYZBase &reference) const
{ {
EulerXYZ<T> result(*this); EulerXYZBase<T> result(*this);
result.x() = AngleRadian<T>(result.x()).wrapped_around(reference.x()).radian(); result.x() = AngleRadianBase<T>(result.x()).wrapped_around(reference.x()).radian();
result.y() = AngleRadian<T>(result.y()).wrapped_around(reference.y()).radian(); result.y() = AngleRadianBase<T>(result.y()).wrapped_around(reference.y()).radian();
result.z() = AngleRadian<T>(result.z()).wrapped_around(reference.z()).radian(); result.z() = AngleRadianBase<T>(result.z()).wrapped_around(reference.z()).radian();
return result; return result;
} }
/** \} */ /** \} */
} // namespace blender::math::detail
namespace blender::math {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Conversion to Quaternions /** \name Conversion to Quaternions
* \{ */ * \{ */
template<typename T> detail::Quaternion<T> to_quaternion(const detail::EulerXYZ<T> &eul) template<typename T> QuaternionBase<T> to_quaternion(const EulerXYZBase<T> &eul)
{ {
using AngleT = typename detail::EulerXYZ<T>::AngleT; using AngleT = typename EulerXYZBase<T>::AngleT;
const AngleT h_angle_i = eul.x() / 2; const AngleT h_angle_i = eul.x() / 2;
const AngleT h_angle_j = eul.y() / 2; const AngleT h_angle_j = eul.y() / 2;
const AngleT h_angle_k = eul.z() / 2; const AngleT h_angle_k = eul.z() / 2;
@ -65,7 +62,7 @@ template<typename T> detail::Quaternion<T> to_quaternion(const detail::EulerXYZ<
const T sin_cos = sin_i * cos_k; const T sin_cos = sin_i * cos_k;
const T sin_sin = sin_i * sin_k; const T sin_sin = sin_i * sin_k;
detail::Quaternion<T> quat; QuaternionBase<T> quat;
quat.w = cos_j * cos_cos + sin_j * sin_sin; quat.w = cos_j * cos_cos + sin_j * sin_sin;
quat.x = cos_j * sin_cos - sin_j * cos_sin; quat.x = cos_j * sin_cos - sin_j * cos_sin;
quat.y = cos_j * sin_sin + sin_j * cos_cos; quat.y = cos_j * sin_sin + sin_j * cos_cos;
@ -73,14 +70,14 @@ template<typename T> detail::Quaternion<T> to_quaternion(const detail::EulerXYZ<
return quat; return quat;
} }
template<typename T> detail::Quaternion<T> to_quaternion(const detail::Euler3<T> &eulO) template<typename T> QuaternionBase<T> to_quaternion(const Euler3Base<T> &eulO)
{ {
/* Swizzle to XYZ. */ /* Swizzle to XYZ. */
detail::EulerXYZ<T> eul_xyz{eulO.ijk()}; EulerXYZBase<T> eul_xyz{eulO.ijk()};
/* Flip with parity. */ /* Flip with parity. */
eul_xyz.y() = eulO.parity() ? -eul_xyz.y() : eul_xyz.y(); eul_xyz.y() = eulO.parity() ? -eul_xyz.y() : eul_xyz.y();
/* Quaternion conversion. */ /* Quaternion conversion. */
detail::Quaternion<T> quat = to_quaternion(eul_xyz); QuaternionBase<T> quat = to_quaternion(eul_xyz);
/* Swizzle back from XYZ. */ /* Swizzle back from XYZ. */
VecBase<T, 3> quat_xyz; VecBase<T, 3> quat_xyz;
quat_xyz[eulO.i_index()] = quat.x; quat_xyz[eulO.i_index()] = quat.x;
@ -97,14 +94,14 @@ template<typename T> detail::Quaternion<T> to_quaternion(const detail::Euler3<T>
* \{ */ * \{ */
template<typename T, typename AngleT = AngleRadian> template<typename T, typename AngleT = AngleRadian>
detail::AxisAngle<T, AngleT> to_axis_angle(const detail::EulerXYZ<T> &euler) AxisAngleBase<T, AngleT> to_axis_angle(const EulerXYZBase<T> &euler)
{ {
/* Use quaternions as intermediate representation for now... */ /* Use quaternions as intermediate representation for now... */
return to_axis_angle<T, AngleT>(to_quaternion(euler)); return to_axis_angle<T, AngleT>(to_quaternion(euler));
} }
template<typename T, typename AngleT = AngleRadian> template<typename T, typename AngleT = AngleRadian>
detail::AxisAngle<T, AngleT> to_axis_angle(const detail::Euler3<T> &euler) AxisAngleBase<T, AngleT> to_axis_angle(const Euler3Base<T> &euler)
{ {
/* Use quaternions as intermediate representation for now... */ /* Use quaternions as intermediate representation for now... */
return to_axis_angle<T, AngleT>(to_quaternion(euler)); return to_axis_angle<T, AngleT>(to_quaternion(euler));

View File

@ -43,14 +43,12 @@ enum EulerOrder {
std::ostream &operator<<(std::ostream &stream, EulerOrder order); std::ostream &operator<<(std::ostream &stream, EulerOrder order);
namespace detail {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name EulerBase /** \name EulerBase
* \{ */ * \{ */
template<typename T> struct EulerBase { template<typename T> struct EulerBase {
using AngleT = AngleRadian<T>; using AngleT = AngleRadianBase<T>;
protected: protected:
/** /**
@ -130,24 +128,24 @@ template<typename T> struct EulerBase {
/** \name EulerXYZ /** \name EulerXYZ
* \{ */ * \{ */
template<typename T> struct EulerXYZ : public EulerBase<T> { template<typename T> struct EulerXYZBase : public EulerBase<T> {
using AngleT = AngleRadian<T>; using AngleT = AngleRadianBase<T>;
public: public:
EulerXYZ() = default; EulerXYZBase() = default;
/** /**
* Create an euler x,y,z rotation from a triple of radian angle. * Create an euler x,y,z rotation from a triple of radian angle.
*/ */
template<typename AngleU> EulerXYZ(const VecBase<AngleU, 3> &vec) : EulerBase<T>(vec){}; template<typename AngleU> EulerXYZBase(const VecBase<AngleU, 3> &vec) : EulerBase<T>(vec){};
EulerXYZ(const AngleT &x, const AngleT &y, const AngleT &z) : EulerBase<T>(x, y, z){}; EulerXYZBase(const AngleT &x, const AngleT &y, const AngleT &z) : EulerBase<T>(x, y, z){};
/** /**
* Create a rotation from an basis axis and an angle. * Create a rotation from an basis axis and an angle.
* This sets a single component of the euler triple, the others are left to 0. * This sets a single component of the euler triple, the others are left to 0.
*/ */
EulerXYZ(const Axis axis, const AngleT &angle) EulerXYZBase(const Axis axis, const AngleT &angle)
{ {
*this = identity(); *this = identity();
this->xyz_[axis] = angle; this->xyz_[axis] = angle;
@ -155,9 +153,11 @@ template<typename T> struct EulerXYZ : public EulerBase<T> {
/** Static functions. */ /** Static functions. */
static EulerXYZ identity() static EulerXYZBase identity()
{ {
return {AngleRadian<T>::identity(), AngleRadian<T>::identity(), AngleRadian<T>::identity()}; return {AngleRadianBase<T>::identity(),
AngleRadianBase<T>::identity(),
AngleRadianBase<T>::identity()};
} }
/** Methods. */ /** Methods. */
@ -165,7 +165,7 @@ template<typename T> struct EulerXYZ : public EulerBase<T> {
/** /**
* Return this euler orientation but with angles wrapped inside [-pi..pi] range. * Return this euler orientation but with angles wrapped inside [-pi..pi] range.
*/ */
EulerXYZ wrapped() const; EulerXYZBase wrapped() const;
/** /**
* Return this euler orientation but wrapped around \a reference. * Return this euler orientation but wrapped around \a reference.
@ -173,21 +173,21 @@ template<typename T> struct EulerXYZ : public EulerBase<T> {
* This means the interpolation between the returned value and \a reference will always take the * This means the interpolation between the returned value and \a reference will always take the
* shortest path. The angle between them will not be more than pi. * shortest path. The angle between them will not be more than pi.
*/ */
EulerXYZ wrapped_around(const EulerXYZ &reference) const; EulerXYZBase wrapped_around(const EulerXYZBase &reference) const;
/** Operators. */ /** Operators. */
friend EulerXYZ operator-(const EulerXYZ &a) friend EulerXYZBase operator-(const EulerXYZBase &a)
{ {
return {-a.xyz_.x, -a.xyz_.y, -a.xyz_.z}; return {-a.xyz_.x, -a.xyz_.y, -a.xyz_.z};
} }
friend bool operator==(const EulerXYZ &a, const EulerXYZ &b) friend bool operator==(const EulerXYZBase &a, const EulerXYZBase &b)
{ {
return a.xyz_ == b.xyz_; return a.xyz_ == b.xyz_;
} }
friend std::ostream &operator<<(std::ostream &stream, const EulerXYZ &rot) friend std::ostream &operator<<(std::ostream &stream, const EulerXYZBase &rot)
{ {
return stream << "EulerXYZ" << static_cast<VecBase<T, 3>>(rot); return stream << "EulerXYZ" << static_cast<VecBase<T, 3>>(rot);
} }
@ -199,8 +199,8 @@ template<typename T> struct EulerXYZ : public EulerBase<T> {
/** \name Euler3 /** \name Euler3
* \{ */ * \{ */
template<typename T> struct Euler3 : public EulerBase<T> { template<typename T> struct Euler3Base : public EulerBase<T> {
using AngleT = AngleRadian<T>; using AngleT = AngleRadianBase<T>;
private: private:
/** Axes order from applying the rotation. */ /** Axes order from applying the rotation. */
@ -211,12 +211,12 @@ template<typename T> struct Euler3 : public EulerBase<T> {
*/ */
class Swizzle { class Swizzle {
private: private:
Euler3 &eul_; Euler3Base &eul_;
public: public:
explicit Swizzle(Euler3 &eul) : eul_(eul){}; explicit Swizzle(Euler3Base &eul) : eul_(eul){};
Euler3 &operator=(const VecBase<AngleT, 3> &angles) Euler3Base &operator=(const VecBase<AngleT, 3> &angles)
{ {
eul_.xyz_.x = angles[eul_.i_index()]; eul_.xyz_.x = angles[eul_.i_index()];
eul_.xyz_.y = angles[eul_.j_index()]; eul_.xyz_.y = angles[eul_.j_index()];
@ -236,7 +236,7 @@ template<typename T> struct Euler3 : public EulerBase<T> {
}; };
public: public:
Euler3() = delete; Euler3Base() = delete;
/** /**
* Create an euler rotation with \a order rotation ordering * Create an euler rotation with \a order rotation ordering
@ -244,16 +244,16 @@ template<typename T> struct Euler3 : public EulerBase<T> {
* eg: If \a order is `EulerOrder::ZXY` then `angles.z` will be the angle of the first rotation. * eg: If \a order is `EulerOrder::ZXY` then `angles.z` will be the angle of the first rotation.
*/ */
template<typename AngleU> template<typename AngleU>
Euler3(const VecBase<AngleU, 3> &angles_xyz, EulerOrder order) Euler3Base(const VecBase<AngleU, 3> &angles_xyz, EulerOrder order)
: EulerBase<T>(angles_xyz), order_(order){}; : EulerBase<T>(angles_xyz), order_(order){};
Euler3(const AngleT &x, const AngleT &y, const AngleT &z, EulerOrder order) Euler3Base(const AngleT &x, const AngleT &y, const AngleT &z, EulerOrder order)
: EulerBase<T>(x, y, z), order_(order){}; : EulerBase<T>(x, y, z), order_(order){};
/** /**
* Create a rotation around a single euler axis and an angle. * Create a rotation around a single euler axis and an angle.
*/ */
Euler3(const Axis axis, AngleT angle, EulerOrder order) : EulerBase<T>(), order_(order) Euler3Base(const Axis axis, AngleT angle, EulerOrder order) : EulerBase<T>(), order_(order)
{ {
this->xyz_[axis] = angle; this->xyz_[axis] = angle;
} }
@ -262,7 +262,7 @@ template<typename T> struct Euler3 : public EulerBase<T> {
* Defines rotation order but not the rotation values. * Defines rotation order but not the rotation values.
* Used for conversion from other rotation types. * Used for conversion from other rotation types.
*/ */
Euler3(EulerOrder order) : order_(order){}; Euler3Base(EulerOrder order) : order_(order){};
/** Methods. */ /** Methods. */
@ -319,24 +319,25 @@ template<typename T> struct Euler3 : public EulerBase<T> {
* This means the interpolation between the returned value and \a reference will always take the * This means the interpolation between the returned value and \a reference will always take the
* shortest path. The angle between them will not be more than pi. * shortest path. The angle between them will not be more than pi.
*/ */
Euler3 wrapped_around(const Euler3 &reference) const Euler3Base wrapped_around(const Euler3Base &reference) const
{ {
return {VecBase<AngleT, 3>(EulerXYZ<T>(this->xyz_).wrapped_around(reference.xyz_)), order_}; return {VecBase<AngleT, 3>(EulerXYZBase<T>(this->xyz_).wrapped_around(reference.xyz_)),
order_};
} }
/** Operators. */ /** Operators. */
friend Euler3 operator-(const Euler3 &a) friend Euler3Base operator-(const Euler3Base &a)
{ {
return {-a.xyz_, a.order_}; return {-a.xyz_, a.order_};
} }
friend bool operator==(const Euler3 &a, const Euler3 &b) friend bool operator==(const Euler3Base &a, const Euler3Base &b)
{ {
return a.xyz_ == b.xyz_ && a.order_ == b.order_; return a.xyz_ == b.xyz_ && a.order_ == b.order_;
} }
friend std::ostream &operator<<(std::ostream &stream, const Euler3 &rot) friend std::ostream &operator<<(std::ostream &stream, const Euler3Base &rot)
{ {
return stream << "Euler3_" << rot.order_ << rot.xyz_; return stream << "Euler3_" << rot.order_ << rot.xyz_;
} }
@ -432,10 +433,8 @@ template<typename T> struct Euler3 : public EulerBase<T> {
/** \} */ /** \} */
} // namespace detail using EulerXYZ = EulerXYZBase<float>;
using Euler3 = Euler3Base<float>;
using EulerXYZ = math::detail::EulerXYZ<float>;
using Euler3 = math::detail::Euler3<float>;
} // namespace blender::math } // namespace blender::math

View File

@ -256,14 +256,12 @@ template<typename MatT, typename VectorT>
* Extract euler rotation from transform matrix. * Extract euler rotation from transform matrix.
* \return the rotation with the smallest values from the potential candidates. * \return the rotation with the smallest values from the potential candidates.
*/ */
template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat);
template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 4, 4> &mat);
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_euler(const MatBase<T, 3, 3> &mat); [[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order);
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_euler(const MatBase<T, 4, 4> &mat); [[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order);
template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order);
template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order);
/** /**
* Extract euler rotation from transform matrix. * Extract euler rotation from transform matrix.
@ -273,25 +271,25 @@ template<typename T>
* \note this correspond to the C API "to_compatible" functions. * \note this correspond to the C API "to_compatible" functions.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_nearest_euler(const MatBase<T, 3, 3> &mat, [[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
const detail::EulerXYZ<T> &reference); const EulerXYZBase<T> &reference);
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_nearest_euler(const MatBase<T, 4, 4> &mat, [[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
const detail::EulerXYZ<T> &reference); const EulerXYZBase<T> &reference);
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_nearest_euler(const MatBase<T, 3, 3> &mat, [[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
const detail::Euler3<T> &reference); const Euler3Base<T> &reference);
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_nearest_euler(const MatBase<T, 4, 4> &mat, [[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
const detail::Euler3<T> &reference); const Euler3Base<T> &reference);
/** /**
* Extract quaternion rotation from transform matrix. * Extract quaternion rotation from transform matrix.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> to_quaternion(const MatBase<T, 3, 3> &mat); [[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 3, 3> &mat);
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> to_quaternion(const MatBase<T, 4, 4> &mat); [[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 4, 4> &mat);
/** /**
* Extract quaternion rotation from transform matrix. * Extract quaternion rotation from transform matrix.
@ -516,25 +514,25 @@ inline bool is_zero(const MatBase<T, NumCol, NumRow> &mat)
namespace detail { namespace detail {
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const AngleRadian<T> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const AngleRadianBase<T> &rotation);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZ<T> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZBase<T> &rotation);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const Euler3<T> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const Euler3Base<T> &rotation);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const Quaternion<T> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const QuaternionBase<T> &rotation);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const DualQuaternion<T> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const DualQuaternionBase<T> &rotation);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const CartesianBasis &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const CartesianBasis &rotation);
template<typename T, int NumCol, int NumRow, typename AngleT> template<typename T, int NumCol, int NumRow, typename AngleT>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const AxisAngle<T, AngleT> &rotation); [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const AxisAngleBase<T, AngleT> &rotation);
} // namespace detail } // namespace detail
@ -591,7 +589,7 @@ template<typename T, int NumCol, int NumRow, typename VectorT>
template<typename T, int NumCol, int NumRow, typename AngleT> template<typename T, int NumCol, int NumRow, typename AngleT>
[[nodiscard]] MatBase<T, NumCol, NumRow> rotate(const MatBase<T, NumCol, NumRow> &mat, [[nodiscard]] MatBase<T, NumCol, NumRow> rotate(const MatBase<T, NumCol, NumRow> &mat,
const detail::AxisAngle<T, AngleT> &rotation) const AxisAngleBase<T, AngleT> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
using Vec3T = typename MatT::vec3_type; using Vec3T = typename MatT::vec3_type;
@ -707,9 +705,7 @@ template<typename T, int NumCol, int NumRow, typename VectorT>
namespace detail { namespace detail {
template<typename T> template<typename T>
void normalized_to_eul2(const MatBase<T, 3, 3> &mat, void normalized_to_eul2(const MatBase<T, 3, 3> &mat, EulerXYZBase<T> &eul1, EulerXYZBase<T> &eul2)
detail::EulerXYZ<T> &eul1,
detail::EulerXYZ<T> &eul2)
{ {
BLI_assert(math::is_unit_scale(mat)); BLI_assert(math::is_unit_scale(mat));
@ -732,9 +728,7 @@ void normalized_to_eul2(const MatBase<T, 3, 3> &mat,
} }
} }
template<typename T> template<typename T>
void normalized_to_eul2(const MatBase<T, 3, 3> &mat, void normalized_to_eul2(const MatBase<T, 3, 3> &mat, Euler3Base<T> &eul1, Euler3Base<T> &eul2)
detail::Euler3<T> &eul1,
detail::Euler3<T> &eul2)
{ {
BLI_assert(math::is_unit_scale(mat)); BLI_assert(math::is_unit_scale(mat));
const int i_index = eul1.i_index(); const int i_index = eul1.i_index();
@ -767,22 +761,22 @@ void normalized_to_eul2(const MatBase<T, 3, 3> &mat,
/* Using explicit template instantiations in order to reduce compilation time. */ /* Using explicit template instantiations in order to reduce compilation time. */
extern template void normalized_to_eul2(const float3x3 &mat, extern template void normalized_to_eul2(const float3x3 &mat,
detail::Euler3<float> &eul1, Euler3Base<float> &eul1,
detail::Euler3<float> &eul2); Euler3Base<float> &eul2);
extern template void normalized_to_eul2(const float3x3 &mat, extern template void normalized_to_eul2(const float3x3 &mat,
detail::EulerXYZ<float> &eul1, EulerXYZBase<float> &eul1,
detail::EulerXYZ<float> &eul2); EulerXYZBase<float> &eul2);
extern template void normalized_to_eul2(const double3x3 &mat, extern template void normalized_to_eul2(const double3x3 &mat,
detail::EulerXYZ<double> &eul1, EulerXYZBase<double> &eul1,
detail::EulerXYZ<double> &eul2); EulerXYZBase<double> &eul2);
template<typename T> detail::Quaternion<T> normalized_to_quat_fast(const MatBase<T, 3, 3> &mat) template<typename T> QuaternionBase<T> normalized_to_quat_fast(const MatBase<T, 3, 3> &mat)
{ {
BLI_assert(math::is_unit_scale(mat)); BLI_assert(math::is_unit_scale(mat));
/* Caller must ensure matrices aren't negative for valid results, see: #24291, #94231. */ /* Caller must ensure matrices aren't negative for valid results, see: #24291, #94231. */
BLI_assert(!math::is_negative(mat)); BLI_assert(!math::is_negative(mat));
detail::Quaternion<T> q; QuaternionBase<T> q;
/* Method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949 /* Method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949
* with an additional `sqrtf(..)` for higher precision result. * with an additional `sqrtf(..)` for higher precision result.
@ -865,12 +859,11 @@ template<typename T> detail::Quaternion<T> normalized_to_quat_fast(const MatBase
return q; return q;
} }
template<typename T> template<typename T> QuaternionBase<T> normalized_to_quat_with_checks(const MatBase<T, 3, 3> &mat)
detail::Quaternion<T> normalized_to_quat_with_checks(const MatBase<T, 3, 3> &mat)
{ {
const T det = math::determinant(mat); const T det = math::determinant(mat);
if (UNLIKELY(!isfinite(det))) { if (UNLIKELY(!isfinite(det))) {
return detail::Quaternion<T>::identity(); return QuaternionBase<T>::identity();
} }
else if (UNLIKELY(det < T(0))) { else if (UNLIKELY(det < T(0))) {
return normalized_to_quat_fast(-mat); return normalized_to_quat_fast(-mat);
@ -879,11 +872,11 @@ detail::Quaternion<T> normalized_to_quat_with_checks(const MatBase<T, 3, 3> &mat
} }
/* Using explicit template instantiations in order to reduce compilation time. */ /* Using explicit template instantiations in order to reduce compilation time. */
extern template Quaternion<float> normalized_to_quat_with_checks(const float3x3 &mat); extern template QuaternionBase<float> normalized_to_quat_with_checks(const float3x3 &mat);
extern template Quaternion<double> normalized_to_quat_with_checks(const double3x3 &mat); extern template QuaternionBase<double> normalized_to_quat_with_checks(const double3x3 &mat);
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZ<T> &rotation) MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZBase<T> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
using DoublePrecision = typename TypeTraits<T>::DoublePrecision; using DoublePrecision = typename TypeTraits<T>::DoublePrecision;
@ -914,14 +907,14 @@ MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZ<T> &rotation)
} }
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
MatBase<T, NumCol, NumRow> from_rotation(const Euler3<T> &rotation) MatBase<T, NumCol, NumRow> from_rotation(const Euler3Base<T> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
const int i_index = rotation.i_index(); const int i_index = rotation.i_index();
const int j_index = rotation.j_index(); const int j_index = rotation.j_index();
const int k_index = rotation.k_index(); const int k_index = rotation.k_index();
#if 1 /* Reference. */ #if 1 /* Reference. */
EulerXYZ<T> euler_xyz(rotation.ijk()); EulerXYZBase<T> euler_xyz(rotation.ijk());
const MatT mat = from_rotation<T, NumCol, NumRow>(rotation.parity() ? -euler_xyz : euler_xyz); const MatT mat = from_rotation<T, NumCol, NumRow>(rotation.parity() ? -euler_xyz : euler_xyz);
MatT result = MatT::identity(); MatT result = MatT::identity();
result[i_index][i_index] = mat[0][0]; result[i_index][i_index] = mat[0][0];
@ -940,7 +933,7 @@ MatBase<T, NumCol, NumRow> from_rotation(const Euler3<T> &rotation)
} }
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
MatBase<T, NumCol, NumRow> from_rotation(const Quaternion<T> &rotation) MatBase<T, NumCol, NumRow> from_rotation(const QuaternionBase<T> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
using DoublePrecision = typename TypeTraits<T>::DoublePrecision; using DoublePrecision = typename TypeTraits<T>::DoublePrecision;
@ -976,7 +969,7 @@ MatBase<T, NumCol, NumRow> from_rotation(const Quaternion<T> &rotation)
/* Not technically speaking a simple rotation, but a whole transform. */ /* Not technically speaking a simple rotation, but a whole transform. */
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const DualQuaternion<T> &rotation) [[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const DualQuaternionBase<T> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
BLI_assert(is_normalized(rotation)); BLI_assert(is_normalized(rotation));
@ -987,8 +980,8 @@ template<typename T, int NumCol, int NumRow>
* Trinity College Dublin, Czech Technical University in Prague * Trinity College Dublin, Czech Technical University in Prague
*/ */
/* Follow the paper notation. */ /* Follow the paper notation. */
const Quaternion<T> &c0 = rotation.quat; const QuaternionBase<T> &c0 = rotation.quat;
const Quaternion<T> &ce = rotation.trans; const QuaternionBase<T> &ce = rotation.trans;
const T &w0 = c0.w, &x0 = c0.x, &y0 = c0.y, &z0 = c0.z; const T &w0 = c0.w, &x0 = c0.x, &y0 = c0.y, &z0 = c0.z;
const T &we = ce.w, &xe = ce.x, &ye = ce.y, &ze = ce.z; const T &we = ce.w, &xe = ce.x, &ye = ce.y, &ze = ce.z;
/* Rotation. */ /* Rotation. */
@ -1016,7 +1009,7 @@ MatBase<T, NumCol, NumRow> from_rotation(const CartesianBasis &rotation)
} }
template<typename T, int NumCol, int NumRow, typename AngleT> template<typename T, int NumCol, int NumRow, typename AngleT>
MatBase<T, NumCol, NumRow> from_rotation(const AxisAngle<T, AngleT> &rotation) MatBase<T, NumCol, NumRow> from_rotation(const AxisAngleBase<T, AngleT> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
using Vec3T = typename MatT::vec3_type; using Vec3T = typename MatT::vec3_type;
@ -1045,7 +1038,7 @@ MatBase<T, NumCol, NumRow> from_rotation(const AxisAngle<T, AngleT> &rotation)
} }
template<typename T, int NumCol, int NumRow> template<typename T, int NumCol, int NumRow>
MatBase<T, NumCol, NumRow> from_rotation(const AngleRadian<T> &rotation) MatBase<T, NumCol, NumRow> from_rotation(const AngleRadianBase<T> &rotation)
{ {
using MatT = MatBase<T, NumCol, NumRow>; using MatT = MatBase<T, NumCol, NumRow>;
const T cos_i = cos(rotation); const T cos_i = cos(rotation);
@ -1061,34 +1054,34 @@ MatBase<T, NumCol, NumRow> from_rotation(const AngleRadian<T> &rotation)
} }
/* Using explicit template instantiations in order to reduce compilation time. */ /* Using explicit template instantiations in order to reduce compilation time. */
extern template MatBase<float, 2, 2> from_rotation(const AngleRadian<float> &rotation); extern template MatBase<float, 2, 2> from_rotation(const AngleRadian &rotation);
extern template MatBase<float, 3, 3> from_rotation(const AngleRadian<float> &rotation); extern template MatBase<float, 3, 3> from_rotation(const AngleRadian &rotation);
extern template MatBase<float, 3, 3> from_rotation(const EulerXYZ<float> &rotation); extern template MatBase<float, 3, 3> from_rotation(const EulerXYZ &rotation);
extern template MatBase<float, 4, 4> from_rotation(const EulerXYZ<float> &rotation); extern template MatBase<float, 4, 4> from_rotation(const EulerXYZ &rotation);
extern template MatBase<float, 3, 3> from_rotation(const Euler3<float> &rotation); extern template MatBase<float, 3, 3> from_rotation(const Euler3 &rotation);
extern template MatBase<float, 4, 4> from_rotation(const Euler3<float> &rotation); extern template MatBase<float, 4, 4> from_rotation(const Euler3 &rotation);
extern template MatBase<float, 3, 3> from_rotation(const Quaternion<float> &rotation); extern template MatBase<float, 3, 3> from_rotation(const Quaternion &rotation);
extern template MatBase<float, 4, 4> from_rotation(const Quaternion<float> &rotation); extern template MatBase<float, 4, 4> from_rotation(const Quaternion &rotation);
extern template MatBase<float, 3, 3> from_rotation(const math::AxisAngle &rotation); extern template MatBase<float, 3, 3> from_rotation(const AxisAngle &rotation);
extern template MatBase<float, 4, 4> from_rotation(const math::AxisAngle &rotation); extern template MatBase<float, 4, 4> from_rotation(const AxisAngle &rotation);
extern template MatBase<float, 3, 3> from_rotation(const math::AxisAngleCartesian &rotation); extern template MatBase<float, 3, 3> from_rotation(const AxisAngleCartesian &rotation);
extern template MatBase<float, 4, 4> from_rotation(const math::AxisAngleCartesian &rotation); extern template MatBase<float, 4, 4> from_rotation(const AxisAngleCartesian &rotation);
} // namespace detail } // namespace detail
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order) [[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order)
{ {
detail::Euler3<T> eul1(order), eul2(order); Euler3Base<T> eul1(order), eul2(order);
detail::normalized_to_eul2(mat, eul1, eul2); detail::normalized_to_eul2(mat, eul1, eul2);
/* Return best, which is just the one with lowest values in it. */ /* Return best, which is just the one with lowest values in it. */
return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 : return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 :
eul1; eul1;
} }
template<typename T> [[nodiscard]] inline detail::EulerXYZ<T> to_euler(const MatBase<T, 3, 3> &mat) template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat)
{ {
detail::EulerXYZ<T> eul1, eul2; EulerXYZBase<T> eul1, eul2;
detail::normalized_to_eul2(mat, eul1, eul2); detail::normalized_to_eul2(mat, eul1, eul2);
/* Return best, which is just the one with lowest values in it. */ /* Return best, which is just the one with lowest values in it. */
return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 : return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 :
@ -1096,23 +1089,23 @@ template<typename T> [[nodiscard]] inline detail::EulerXYZ<T> to_euler(const Mat
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order) [[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order)
{ {
/* TODO(fclem): Avoid the copy with 3x3 ref. */ /* TODO(fclem): Avoid the copy with 3x3 ref. */
return to_euler<T>(MatBase<T, 3, 3>(mat), order); return to_euler<T>(MatBase<T, 3, 3>(mat), order);
} }
template<typename T> [[nodiscard]] inline detail::EulerXYZ<T> to_euler(const MatBase<T, 4, 4> &mat) template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 4, 4> &mat)
{ {
/* TODO(fclem): Avoid the copy with 3x3 ref. */ /* TODO(fclem): Avoid the copy with 3x3 ref. */
return to_euler<T>(MatBase<T, 3, 3>(mat)); return to_euler<T>(MatBase<T, 3, 3>(mat));
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_nearest_euler(const MatBase<T, 3, 3> &mat, [[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
const detail::Euler3<T> &reference) const Euler3Base<T> &reference)
{ {
detail::Euler3<T> eul1(reference.order()), eul2(reference.order()); Euler3Base<T> eul1(reference.order()), eul2(reference.order());
detail::normalized_to_eul2(mat, eul1, eul2); detail::normalized_to_eul2(mat, eul1, eul2);
eul1 = eul1.wrapped_around(reference); eul1 = eul1.wrapped_around(reference);
eul2 = eul2.wrapped_around(reference); eul2 = eul2.wrapped_around(reference);
@ -1124,10 +1117,10 @@ template<typename T>
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_nearest_euler(const MatBase<T, 3, 3> &mat, [[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
const detail::EulerXYZ<T> &reference) const EulerXYZBase<T> &reference)
{ {
detail::EulerXYZ<T> eul1, eul2; EulerXYZBase<T> eul1, eul2;
detail::normalized_to_eul2(mat, eul1, eul2); detail::normalized_to_eul2(mat, eul1, eul2);
eul1 = eul1.wrapped_around(reference); eul1 = eul1.wrapped_around(reference);
eul2 = eul2.wrapped_around(reference); eul2 = eul2.wrapped_around(reference);
@ -1139,29 +1132,29 @@ template<typename T>
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Euler3<T> to_nearest_euler(const MatBase<T, 4, 4> &mat, [[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
const detail::Euler3<T> &reference) const Euler3Base<T> &reference)
{ {
/* TODO(fclem): Avoid the copy with 3x3 ref. */ /* TODO(fclem): Avoid the copy with 3x3 ref. */
return to_euler<T>(MatBase<T, 3, 3>(mat), reference); return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::EulerXYZ<T> to_nearest_euler(const MatBase<T, 4, 4> &mat, [[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
const detail::EulerXYZ<T> &reference) const EulerXYZBase<T> &reference)
{ {
/* TODO(fclem): Avoid the copy with 3x3 ref. */ /* TODO(fclem): Avoid the copy with 3x3 ref. */
return to_euler<T>(MatBase<T, 3, 3>(mat), reference); return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> to_quaternion(const MatBase<T, 3, 3> &mat) [[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 3, 3> &mat)
{ {
return detail::normalized_to_quat_with_checks(mat); return detail::normalized_to_quat_with_checks(mat);
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> to_quaternion(const MatBase<T, 4, 4> &mat) [[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 4, 4> &mat)
{ {
/* TODO(fclem): Avoid the copy with 3x3 ref. */ /* TODO(fclem): Avoid the copy with 3x3 ref. */
return to_quaternion<T>(MatBase<T, 3, 3>(mat)); return to_quaternion<T>(MatBase<T, 3, 3>(mat));
@ -1195,19 +1188,19 @@ template<bool AllowNegativeScale, typename T>
namespace detail { namespace detail {
template<typename T> template<typename T>
inline void to_rotation(const MatBase<T, 3, 3> &mat, detail::Quaternion<T> &r_rotation) inline void to_rotation(const MatBase<T, 3, 3> &mat, QuaternionBase<T> &r_rotation)
{ {
r_rotation = to_quaternion<T>(mat); r_rotation = to_quaternion<T>(mat);
} }
template<typename T> template<typename T>
inline void to_rotation(const MatBase<T, 3, 3> &mat, detail::EulerXYZ<T> &r_rotation) inline void to_rotation(const MatBase<T, 3, 3> &mat, EulerXYZBase<T> &r_rotation)
{ {
r_rotation = to_euler<T>(mat); r_rotation = to_euler<T>(mat);
} }
template<typename T> template<typename T>
inline void to_rotation(const MatBase<T, 3, 3> &mat, detail::Euler3<T> &r_rotation) inline void to_rotation(const MatBase<T, 3, 3> &mat, Euler3Base<T> &r_rotation)
{ {
r_rotation = to_euler<T>(mat, r_rotation.order()); r_rotation = to_euler<T>(mat, r_rotation.order());
} }

View File

@ -24,37 +24,34 @@ namespace blender::math {
* Equivalent to component wise multiplication followed by summation of the result. * Equivalent to component wise multiplication followed by summation of the result.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline T dot(const detail::Quaternion<T> &a, const detail::Quaternion<T> &b); [[nodiscard]] inline T dot(const QuaternionBase<T> &a, const QuaternionBase<T> &b);
/** /**
* Raise a unit #Quaternion \a q to the real \a y exponent. * Raise a unit #Quaternion \a q to the real \a y exponent.
* \note This only works on unit quaternions and y != 0. * \note This only works on unit quaternions and y != 0.
* \note This is not a per component power. * \note This is not a per component power.
*/ */
template<typename T> template<typename T> [[nodiscard]] QuaternionBase<T> pow(const QuaternionBase<T> &q, const T &y);
[[nodiscard]] detail::Quaternion<T> pow(const detail::Quaternion<T> &q, const T &y);
/** /**
* Return the conjugate of the given quaternion. * Return the conjugate of the given quaternion.
* If the quaternion \a q represent the rotation from A to B, * If the quaternion \a q represent the rotation from A to B,
* then the conjugate of \a q represents the rotation from B to A. * then the conjugate of \a q represents the rotation from B to A.
*/ */
template<typename T> template<typename T> [[nodiscard]] inline QuaternionBase<T> conjugate(const QuaternionBase<T> &a);
[[nodiscard]] inline detail::Quaternion<T> conjugate(const detail::Quaternion<T> &a);
/** /**
* Negate the quaternion if real component (w) is negative. * Negate the quaternion if real component (w) is negative.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> canonicalize(const detail::Quaternion<T> &q); [[nodiscard]] inline QuaternionBase<T> canonicalize(const QuaternionBase<T> &q);
/** /**
* Return invert of \a q or identity if \a q is ill-formed. * Return invert of \a q or identity if \a q is ill-formed.
* The invert allows quaternion division. * The invert allows quaternion division.
* \note The inverse of \a q isn't the opposite rotation. This would be the conjugate. * \note The inverse of \a q isn't the opposite rotation. This would be the conjugate.
*/ */
template<typename T> template<typename T> [[nodiscard]] inline QuaternionBase<T> invert(const QuaternionBase<T> &q);
[[nodiscard]] inline detail::Quaternion<T> invert(const detail::Quaternion<T> &q);
/** /**
* Return invert of \a q assuming it is a unit quaternion. * Return invert of \a q assuming it is a unit quaternion.
@ -62,26 +59,25 @@ template<typename T>
* but this function shows the intent better, and asserts if \a q ever becomes non-unit-length. * but this function shows the intent better, and asserts if \a q ever becomes non-unit-length.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> invert_normalized(const detail::Quaternion<T> &q); [[nodiscard]] inline QuaternionBase<T> invert_normalized(const QuaternionBase<T> &q);
/** /**
* Return a unit quaternion representing the same rotation as \a q or * Return a unit quaternion representing the same rotation as \a q or
* the identity quaternion if \a q is ill-formed. * the identity quaternion if \a q is ill-formed.
*/ */
template<typename T> [[nodiscard]] inline QuaternionBase<T> normalize(const QuaternionBase<T> &q);
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> normalize(const detail::Quaternion<T> &q); [[nodiscard]] inline QuaternionBase<T> normalize_and_get_length(const QuaternionBase<T> &q,
template<typename T> T &out_length);
[[nodiscard]] inline detail::Quaternion<T> normalize_and_get_length(const detail::Quaternion<T> &q,
T &out_length);
/** /**
* Use spherical interpolation between two quaternions. * Use spherical interpolation between two quaternions.
* Always interpolate along the shortest angle. * Always interpolate along the shortest angle.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> interpolate(const detail::Quaternion<T> &a, [[nodiscard]] inline QuaternionBase<T> interpolate(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b, const QuaternionBase<T> &b,
T t); T t);
/** \} */ /** \} */
@ -93,7 +89,7 @@ template<typename T>
* Transform \a v by rotation using the quaternion \a q . * Transform \a v by rotation using the quaternion \a q .
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline VecBase<T, 3> transform_point(const detail::Quaternion<T> &q, [[nodiscard]] inline VecBase<T, 3> transform_point(const QuaternionBase<T> &q,
const VecBase<T, 3> &v); const VecBase<T, 3> &v);
/** \} */ /** \} */
@ -105,7 +101,7 @@ template<typename T>
/** /**
* Returns true if all components are exactly equal to 0. * Returns true if all components are exactly equal to 0.
*/ */
template<typename T> [[nodiscard]] inline bool is_zero(const detail::Quaternion<T> &q) template<typename T> [[nodiscard]] inline bool is_zero(const QuaternionBase<T> &q)
{ {
return q.w == T(0) && q.x == T(0) && q.y == T(0) && q.z == T(0); return q.w == T(0) && q.x == T(0) && q.y == T(0) && q.z == T(0);
} }
@ -114,22 +110,22 @@ template<typename T> [[nodiscard]] inline bool is_zero(const detail::Quaternion<
* Returns true if the quaternions are equal within the given epsilon. Return false otherwise. * Returns true if the quaternions are equal within the given epsilon. Return false otherwise.
*/ */
template<typename T> template<typename T>
[[nodiscard]] inline bool is_equal(const detail::Quaternion<T> &a, [[nodiscard]] inline bool is_equal(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b, const QuaternionBase<T> &b,
const T epsilon = T(0)) const T epsilon = T(0))
{ {
return math::abs(a.w - b.w) <= epsilon && math::abs(a.y - b.y) <= epsilon && return math::abs(a.w - b.w) <= epsilon && math::abs(a.y - b.y) <= epsilon &&
math::abs(a.x - b.x) <= epsilon && math::abs(a.z - b.z) <= epsilon; math::abs(a.x - b.x) <= epsilon && math::abs(a.z - b.z) <= epsilon;
} }
template<typename T> [[nodiscard]] inline bool is_unit_scale(const detail::Quaternion<T> &q) template<typename T> [[nodiscard]] inline bool is_unit_scale(const QuaternionBase<T> &q)
{ {
/* Checks are flipped so NAN doesn't assert because we're making sure the value was /* Checks are flipped so NAN doesn't assert because we're making sure the value was
* normalized and in the case we don't want NAN to be raising asserts since there * normalized and in the case we don't want NAN to be raising asserts since there
* is nothing to be done in that case. */ * is nothing to be done in that case. */
const T test_unit = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; const T test_unit = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
return (!(math::abs(test_unit - T(1)) >= AssertUnitEpsilon<detail::Quaternion<T>>::value) || return (!(math::abs(test_unit - T(1)) >= AssertUnitEpsilon<QuaternionBase<T>>::value) ||
!(math::abs(test_unit) >= AssertUnitEpsilon<detail::Quaternion<T>>::value)); !(math::abs(test_unit) >= AssertUnitEpsilon<QuaternionBase<T>>::value));
} }
/** \} */ /** \} */
@ -138,73 +134,69 @@ template<typename T> [[nodiscard]] inline bool is_unit_scale(const detail::Quate
/** \name Quaternion /** \name Quaternion
* \{ */ * \{ */
namespace detail {
/* -------------- Conversions -------------- */ /* -------------- Conversions -------------- */
template<typename T> AngleRadian<T> Quaternion<T>::twist_angle(const Axis axis) const template<typename T> AngleRadianBase<T> QuaternionBase<T>::twist_angle(const Axis axis) const
{ {
/* The calculation requires a canonical quaternion. */ /* The calculation requires a canonical quaternion. */
const VecBase<T, 4> input_vec(canonicalize(*this)); const VecBase<T, 4> input_vec(canonicalize(*this));
return T(2) * AngleRadian<T>(input_vec[0], input_vec.yzw()[axis.as_int()]); return T(2) * AngleRadianBase<T>(input_vec[0], input_vec.yzw()[axis.as_int()]);
} }
template<typename T> Quaternion<T> Quaternion<T>::swing(const Axis axis) const template<typename T> QuaternionBase<T> QuaternionBase<T>::swing(const Axis axis) const
{ {
/* The calculation requires a canonical quaternion. */ /* The calculation requires a canonical quaternion. */
const Quaternion<T> input = canonicalize(*this); const QuaternionBase<T> input = canonicalize(*this);
/* Compute swing by multiplying the original quaternion by inverted twist. */ /* Compute swing by multiplying the original quaternion by inverted twist. */
Quaternion<T> swing = input * invert_normalized(input.twist(axis)); QuaternionBase<T> swing = input * invert_normalized(input.twist(axis));
BLI_assert(math::abs(VecBase<T, 4>(swing)[axis.as_int() + 1]) < BLI_ASSERT_UNIT_EPSILON); BLI_assert(math::abs(VecBase<T, 4>(swing)[axis.as_int() + 1]) < BLI_ASSERT_UNIT_EPSILON);
return swing; return swing;
} }
template<typename T> Quaternion<T> Quaternion<T>::twist(const Axis axis) const template<typename T> QuaternionBase<T> QuaternionBase<T>::twist(const Axis axis) const
{ {
/* The calculation requires a canonical quaternion. */ /* The calculation requires a canonical quaternion. */
const VecBase<T, 4> input_vec(canonicalize(*this)); const VecBase<T, 4> input_vec(canonicalize(*this));
AngleCartesian<T> half_angle = AngleCartesian<T>::from_point(input_vec[0], AngleCartesianBase<T> half_angle = AngleCartesianBase<T>::from_point(
input_vec.yzw()[axis.as_int()]); input_vec[0], input_vec.yzw()[axis.as_int()]);
VecBase<T, 4> twist(half_angle.cos(), T(0), T(0), T(0)); VecBase<T, 4> twist(half_angle.cos(), T(0), T(0), T(0));
twist[axis.as_int() + 1] = half_angle.sin(); twist[axis.as_int() + 1] = half_angle.sin();
return Quaternion<T>(twist); return QuaternionBase<T>(twist);
} }
/* -------------- Methods -------------- */ /* -------------- Methods -------------- */
template<typename T> Quaternion<T> Quaternion<T>::wrapped_around(const Quaternion &reference) const template<typename T>
QuaternionBase<T> QuaternionBase<T>::wrapped_around(const QuaternionBase<T> &reference) const
{ {
BLI_assert(is_unit_scale(*this)); BLI_assert(is_unit_scale(*this));
const Quaternion<T> &input = *this; const QuaternionBase<T> &input = *this;
T len; T len;
Quaternion<T> reference_normalized = normalize_and_get_length(reference, len); QuaternionBase<T> reference_normalized = normalize_and_get_length(reference, len);
/* Skips degenerate case. */ /* Skips degenerate case. */
if (len < 1e-4f) { if (len < 1e-4f) {
return input; return input;
} }
Quaternion<T> result = reference * invert_normalized(reference_normalized) * input; QuaternionBase<T> result = reference * invert_normalized(reference_normalized) * input;
return (distance_squared(VecBase<T, 4>(-result), VecBase<T, 4>(reference)) < return (distance_squared(VecBase<T, 4>(-result), VecBase<T, 4>(reference)) <
distance_squared(VecBase<T, 4>(result), VecBase<T, 4>(reference))) ? distance_squared(VecBase<T, 4>(result), VecBase<T, 4>(reference))) ?
-result : -result :
result; result;
} }
} // namespace detail
/* -------------- Functions -------------- */ /* -------------- Functions -------------- */
template<typename T> template<typename T>
[[nodiscard]] inline T dot(const detail::Quaternion<T> &a, const detail::Quaternion<T> &b) [[nodiscard]] inline T dot(const QuaternionBase<T> &a, const QuaternionBase<T> &b)
{ {
return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z; return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
} }
template<typename T> template<typename T> [[nodiscard]] QuaternionBase<T> pow(const QuaternionBase<T> &q, const T &y)
[[nodiscard]] detail::Quaternion<T> pow(const detail::Quaternion<T> &q, const T &y)
{ {
BLI_assert(is_unit_scale(q)); BLI_assert(is_unit_scale(q));
/* Reference material: /* Reference material:
@ -223,45 +215,42 @@ template<typename T>
return {math::cos(half_angle), math::sin(half_angle) * normalize(q.imaginary_part())}; return {math::cos(half_angle), math::sin(half_angle) * normalize(q.imaginary_part())};
} }
template<typename T> template<typename T> [[nodiscard]] inline QuaternionBase<T> conjugate(const QuaternionBase<T> &a)
[[nodiscard]] inline detail::Quaternion<T> conjugate(const detail::Quaternion<T> &a)
{ {
return {a.w, -a.x, -a.y, -a.z}; return {a.w, -a.x, -a.y, -a.z};
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> canonicalize(const detail::Quaternion<T> &q) [[nodiscard]] inline QuaternionBase<T> canonicalize(const QuaternionBase<T> &q)
{ {
return (q.w < T(0)) ? -q : q; return (q.w < T(0)) ? -q : q;
} }
template<typename T> template<typename T> [[nodiscard]] inline QuaternionBase<T> invert(const QuaternionBase<T> &q)
[[nodiscard]] inline detail::Quaternion<T> invert(const detail::Quaternion<T> &q)
{ {
const T length_squared = dot(q, q); const T length_squared = dot(q, q);
if (length_squared == T(0)) { if (length_squared == T(0)) {
return detail::Quaternion<T>::identity(); return QuaternionBase<T>::identity();
} }
return conjugate(q) * (T(1) / length_squared); return conjugate(q) * (T(1) / length_squared);
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> invert_normalized(const detail::Quaternion<T> &q) [[nodiscard]] inline QuaternionBase<T> invert_normalized(const QuaternionBase<T> &q)
{ {
BLI_assert(is_unit_scale(q)); BLI_assert(is_unit_scale(q));
return conjugate(q); return conjugate(q);
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> normalize_and_get_length(const detail::Quaternion<T> &q, [[nodiscard]] inline QuaternionBase<T> normalize_and_get_length(const QuaternionBase<T> &q,
T &out_length) T &out_length)
{ {
out_length = math::sqrt(dot(q, q)); out_length = math::sqrt(dot(q, q));
return (out_length != T(0)) ? (q * (T(1) / out_length)) : detail::Quaternion<T>::identity(); return (out_length != T(0)) ? (q * (T(1) / out_length)) : QuaternionBase<T>::identity();
} }
template<typename T> template<typename T> [[nodiscard]] inline QuaternionBase<T> normalize(const QuaternionBase<T> &q)
[[nodiscard]] inline detail::Quaternion<T> normalize(const detail::Quaternion<T> &q)
{ {
T len; T len;
return normalize_and_get_length(q, len); return normalize_and_get_length(q, len);
@ -306,28 +295,28 @@ template<typename T>
} }
template<typename T> template<typename T>
[[nodiscard]] inline detail::Quaternion<T> interpolate(const detail::Quaternion<T> &a, [[nodiscard]] inline QuaternionBase<T> interpolate(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b, const QuaternionBase<T> &b,
T t) T t)
{ {
using Vec4T = VecBase<T, 4>; using Vec4T = VecBase<T, 4>;
BLI_assert(is_unit_scale(a)); BLI_assert(is_unit_scale(a));
BLI_assert(is_unit_scale(b)); BLI_assert(is_unit_scale(b));
VecBase<T, 2> w = interpolate_dot_slerp(t, dot(a, b)); VecBase<T, 2> w = interpolate_dot_slerp(t, dot(a, b));
return detail::Quaternion<T>(w[0] * Vec4T(a) + w[1] * Vec4T(b)); return QuaternionBase<T>(w[0] * Vec4T(a) + w[1] * Vec4T(b));
} }
template<typename T> template<typename T>
[[nodiscard]] inline VecBase<T, 3> transform_point(const detail::Quaternion<T> &q, [[nodiscard]] inline VecBase<T, 3> transform_point(const QuaternionBase<T> &q,
const VecBase<T, 3> &v) const VecBase<T, 3> &v)
{ {
#if 0 /* Reference. */ #if 0 /* Reference. */
detail::Quaternion<T> V(T(0), UNPACK3(v)); QuaternionBase<T> V(T(0), UNPACK3(v));
detail::Quaternion<T> R = q * V * conjugate(q); QuaternionBase<T> R = q * V * conjugate(q);
return {R.x, R.y, R.z}; return {R.x, R.y, R.z};
#else #else
/* `S = q * V` */ /* `S = q * V` */
detail::Quaternion<T> S; QuaternionBase<T> S;
S.w = /* q.w * 0.0 */ -q.x * v.x - q.y * v.y - q.z * v.z; S.w = /* q.w * 0.0 */ -q.x * v.x - q.y * v.y - q.z * v.z;
S.x = q.w * v.x /* + q.x * 0.0 */ + q.y * v.z - q.z * v.y; S.x = q.w * v.x /* + q.x * 0.0 */ + q.y * v.z - q.z * v.y;
S.y = q.w * v.y /* + q.y * 0.0 */ + q.z * v.x - q.x * v.z; S.y = q.w * v.y /* + q.y * 0.0 */ + q.z * v.x - q.x * v.z;
@ -348,21 +337,20 @@ template<typename T>
/** \name Dual-Quaternion /** \name Dual-Quaternion
* \{ */ * \{ */
namespace detail {
/* -------------- Constructors -------------- */ /* -------------- Constructors -------------- */
template<typename T> template<typename T>
DualQuaternion<T>::DualQuaternion(const Quaternion<T> &non_dual, const Quaternion<T> &dual) DualQuaternionBase<T>::DualQuaternionBase(const QuaternionBase<T> &non_dual,
const QuaternionBase<T> &dual)
: quat(non_dual), trans(dual), scale_weight(0), quat_weight(1) : quat(non_dual), trans(dual), scale_weight(0), quat_weight(1)
{ {
BLI_assert(is_unit_scale(non_dual)); BLI_assert(is_unit_scale(non_dual));
} }
template<typename T> template<typename T>
DualQuaternion<T>::DualQuaternion(const Quaternion<T> &non_dual, DualQuaternionBase<T>::DualQuaternionBase(const QuaternionBase<T> &non_dual,
const Quaternion<T> &dual, const QuaternionBase<T> &dual,
const MatBase<T, 4, 4> &scale_mat) const MatBase<T, 4, 4> &scale_mat)
: quat(non_dual), trans(dual), scale(scale_mat), scale_weight(1), quat_weight(1) : quat(non_dual), trans(dual), scale(scale_mat), scale_weight(1), quat_weight(1)
{ {
BLI_assert(is_unit_scale(non_dual)); BLI_assert(is_unit_scale(non_dual));
@ -370,9 +358,10 @@ DualQuaternion<T>::DualQuaternion(const Quaternion<T> &non_dual,
/* -------------- Operators -------------- */ /* -------------- Operators -------------- */
template<typename T> DualQuaternion<T> &DualQuaternion<T>::operator+=(const DualQuaternion<T> &b) template<typename T>
DualQuaternionBase<T> &DualQuaternionBase<T>::operator+=(const DualQuaternionBase<T> &b)
{ {
DualQuaternion<T> &a = *this; DualQuaternionBase<T> &a = *this;
/* Sum rotation and translation. */ /* Sum rotation and translation. */
/* Make sure we interpolate quaternions in the right direction. */ /* Make sure we interpolate quaternions in the right direction. */
@ -416,10 +405,10 @@ template<typename T> DualQuaternion<T> &DualQuaternion<T>::operator+=(const Dual
return *this; return *this;
} }
template<typename T> DualQuaternion<T> &DualQuaternion<T>::operator*=(const T &t) template<typename T> DualQuaternionBase<T> &DualQuaternionBase<T>::operator*=(const T &t)
{ {
BLI_assert(t >= 0); BLI_assert(t >= 0);
DualQuaternion<T> &q = *this; DualQuaternionBase<T> &q = *this;
q.quat.w *= t; q.quat.w *= t;
q.quat.x *= t; q.quat.x *= t;
@ -440,8 +429,6 @@ template<typename T> DualQuaternion<T> &DualQuaternion<T>::operator*=(const T &t
return *this; return *this;
} }
} // namespace detail
/* -------------- Functions -------------- */ /* -------------- Functions -------------- */
/** /**
@ -452,18 +439,18 @@ template<typename T> DualQuaternion<T> &DualQuaternion<T>::operator*=(const T &t
* \note Returns identity #DualQuaternion if degenerate. * \note Returns identity #DualQuaternion if degenerate.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::DualQuaternion<T> normalize(const detail::DualQuaternion<T> &dual_quat) [[nodiscard]] DualQuaternionBase<T> normalize(const DualQuaternionBase<T> &dual_quat)
{ {
const T norm_weighted = math::sqrt(dot(dual_quat.quat, dual_quat.quat)); const T norm_weighted = math::sqrt(dot(dual_quat.quat, dual_quat.quat));
/* NOTE(fclem): Should this be an epsilon? */ /* NOTE(fclem): Should this be an epsilon? */
if (norm_weighted == T(0)) { if (norm_weighted == T(0)) {
/* The dual-quaternion was zero initialized or is degenerate. Return identity. */ /* The dual-quaternion was zero initialized or is degenerate. Return identity. */
return detail::DualQuaternion<T>::identity(); return DualQuaternionBase<T>::identity();
} }
const T inv_norm_weighted = T(1) / norm_weighted; const T inv_norm_weighted = T(1) / norm_weighted;
detail::DualQuaternion<T> dq = dual_quat; DualQuaternionBase<T> dq = dual_quat;
dq.quat = dq.quat * inv_norm_weighted; dq.quat = dq.quat * inv_norm_weighted;
dq.trans = dq.trans * inv_norm_weighted; dq.trans = dq.trans * inv_norm_weighted;
@ -493,7 +480,7 @@ template<typename T>
* first. Optionally outputs crazy space matrix. * first. Optionally outputs crazy space matrix.
*/ */
template<typename T> template<typename T>
[[nodiscard]] VecBase<T, 3> transform_point(const detail::DualQuaternion<T> &dq, [[nodiscard]] VecBase<T, 3> transform_point(const DualQuaternionBase<T> &dq,
const VecBase<T, 3> &point, const VecBase<T, 3> &point,
MatBase<T, 3, 3> *r_crazy_space_mat = nullptr) MatBase<T, 3, 3> *r_crazy_space_mat = nullptr)
{ {
@ -555,8 +542,8 @@ template<typename T>
* This allows volume preserving deformation for skinning. * This allows volume preserving deformation for skinning.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::DualQuaternion<T> to_dual_quaternion(const MatBase<T, 4, 4> &mat, [[nodiscard]] DualQuaternionBase<T> to_dual_quaternion(const MatBase<T, 4, 4> &mat,
const MatBase<T, 4, 4> &basemat) const MatBase<T, 4, 4> &basemat)
{ {
/** /**
* Conversion routines between (regular quaternion, translation) and dual quaternion. * Conversion routines between (regular quaternion, translation) and dual quaternion.
@ -586,7 +573,7 @@ template<typename T>
const Mat4T baseinv = invert(basemat); const Mat4T baseinv = invert(basemat);
/* Extra orthogonalize, to avoid flipping with stretched bones. */ /* Extra orthogonalize, to avoid flipping with stretched bones. */
detail::Quaternion<T> basequat = to_quaternion(normalize(orthogonalize(baseRS, Axis::Y))); QuaternionBase<T> basequat = to_quaternion(normalize(orthogonalize(baseRS, Axis::Y)));
Mat4T baseR = from_rotation<Mat4T>(basequat); Mat4T baseR = from_rotation<Mat4T>(basequat);
baseR.location() = baseRS.location(); baseR.location() = baseRS.location();
@ -603,21 +590,21 @@ template<typename T>
} }
/* Non-dual part. */ /* Non-dual part. */
const detail::Quaternion<T> q = to_quaternion(normalize(R)); const QuaternionBase<T> q = to_quaternion(normalize(R));
/* Dual part. */ /* Dual part. */
const Vec3T &t = R.location().xyz(); const Vec3T &t = R.location().xyz();
detail::Quaternion<T> d; QuaternionBase<T> d;
d.w = T(-0.5) * (+t.x * q.x + t.y * q.y + t.z * q.z); d.w = T(-0.5) * (+t.x * q.x + t.y * q.y + t.z * q.z);
d.x = T(+0.5) * (+t.x * q.w + t.y * q.z - t.z * q.y); d.x = T(+0.5) * (+t.x * q.w + t.y * q.z - t.z * q.y);
d.y = T(+0.5) * (-t.x * q.z + t.y * q.w + t.z * q.x); d.y = T(+0.5) * (-t.x * q.z + t.y * q.w + t.z * q.x);
d.z = T(+0.5) * (+t.x * q.y - t.y * q.x + t.z * q.w); d.z = T(+0.5) * (+t.x * q.y - t.y * q.x + t.z * q.w);
if (has_scale) { if (has_scale) {
return detail::DualQuaternion<T>(q, d, scale); return DualQuaternionBase<T>(q, d, scale);
} }
return detail::DualQuaternion<T>(q, d); return DualQuaternionBase<T>(q, d);
} }
/** \} */ /** \} */
@ -631,7 +618,7 @@ namespace blender::math {
* \{ */ * \{ */
template<typename T, typename AngleT = AngleRadian> template<typename T, typename AngleT = AngleRadian>
detail::AxisAngle<T, AngleT> to_axis_angle(const detail::Quaternion<T> &quat) AxisAngleBase<T, AngleT> to_axis_angle(const QuaternionBase<T> &quat)
{ {
BLI_assert(is_unit_scale(quat)); BLI_assert(is_unit_scale(quat));
@ -649,7 +636,7 @@ detail::AxisAngle<T, AngleT> to_axis_angle(const detail::Quaternion<T> &quat)
/* Leverage AngleT implementation of double angle. */ /* Leverage AngleT implementation of double angle. */
AngleT angle = AngleT(cos_half_angle, sin_half_angle) * 2; AngleT angle = AngleT(cos_half_angle, sin_half_angle) * 2;
return detail::AxisAngle<T, AngleT>(axis, angle); return AxisAngleBase<T, AngleT>(axis, angle);
} }
/** \} */ /** \} */
@ -658,7 +645,7 @@ detail::AxisAngle<T, AngleT> to_axis_angle(const detail::Quaternion<T> &quat)
/** \name Conversion to Euler /** \name Conversion to Euler
* \{ */ * \{ */
template<typename T> detail::EulerXYZ<T> to_euler(const detail::Quaternion<T> &quat) template<typename T> EulerXYZBase<T> to_euler(const QuaternionBase<T> &quat)
{ {
using Mat3T = MatBase<T, 3, 3>; using Mat3T = MatBase<T, 3, 3>;
BLI_assert(is_unit_scale(quat)); BLI_assert(is_unit_scale(quat));
@ -666,8 +653,7 @@ template<typename T> detail::EulerXYZ<T> to_euler(const detail::Quaternion<T> &q
return to_euler<T>(unit_mat); return to_euler<T>(unit_mat);
} }
template<typename T> template<typename T> Euler3Base<T> to_euler(const QuaternionBase<T> &quat, EulerOrder order)
detail::Euler3<T> to_euler(const detail::Quaternion<T> &quat, EulerOrder order)
{ {
using Mat3T = MatBase<T, 3, 3>; using Mat3T = MatBase<T, 3, 3>;
BLI_assert(is_unit_scale(quat)); BLI_assert(is_unit_scale(quat));
@ -683,24 +669,23 @@ detail::Euler3<T> to_euler(const detail::Quaternion<T> &quat, EulerOrder order)
/* Prototype needed to avoid interdependencies of headers. */ /* Prototype needed to avoid interdependencies of headers. */
template<typename T, typename AngleT> template<typename T, typename AngleT>
detail::Quaternion<T> to_quaternion(const detail::AxisAngle<T, AngleT> &axis_angle); QuaternionBase<T> to_quaternion(const AxisAngleBase<T, AngleT> &axis_angle);
template<typename T> template<typename T> QuaternionBase<T> QuaternionBase<T>::expmap(const VecBase<T, 3> &expmap)
detail::Quaternion<T> detail::Quaternion<T>::expmap(const VecBase<T, 3> &expmap)
{ {
using AxisAngleT = detail::AxisAngle<T, detail::AngleRadian<T>>; using AxisAngleT = AxisAngleBase<T, AngleRadianBase<T>>;
/* Obtain axis/angle representation. */ /* Obtain axis/angle representation. */
T angle; T angle;
const VecBase<T, 3> axis = normalize_and_get_length(expmap, angle); const VecBase<T, 3> axis = normalize_and_get_length(expmap, angle);
if (LIKELY(angle != T(0))) { if (LIKELY(angle != T(0))) {
return to_quaternion(AxisAngleT(axis, angle_wrap_rad(angle))); return to_quaternion(AxisAngleT(axis, angle_wrap_rad(angle)));
} }
return detail::Quaternion<T>::identity(); return QuaternionBase<T>::identity();
} }
template<typename T> VecBase<T, 3> detail::Quaternion<T>::expmap() const template<typename T> VecBase<T, 3> QuaternionBase<T>::expmap() const
{ {
using AxisAngleT = detail::AxisAngle<T, detail::AngleRadian<T>>; using AxisAngleT = AxisAngleBase<T, AngleRadianBase<T>>;
BLI_assert(is_unit_scale(*this)); BLI_assert(is_unit_scale(*this));
const AxisAngleT axis_angle = to_axis_angle(*this); const AxisAngleT axis_angle = to_axis_angle(*this);
return axis_angle.axis() * axis_angle.angle().radian(); return axis_angle.axis() * axis_angle.angle().radian();

View File

@ -18,48 +18,46 @@ namespace blender::math {
/** \name Quaternion /** \name Quaternion
* \{ */ * \{ */
namespace detail {
/** /**
* A `blender::math::Quaternion<T>` represents either an orientation or a rotation. * A `blender::math::QuaternionBase<T>` represents either an orientation or a rotation.
* *
* Mainly used for rigging and armature deformations as they have nice mathematical properties * Mainly used for rigging and armature deformations as they have nice mathematical properties
* (eg: smooth shortest path interpolation). A `blender::math::Quaternion<T>` is cheaper to combine * (eg: smooth shortest path interpolation). A `blender::math::QuaternionBase<T>` is cheaper to
* than `MatBase<T, 3, 3>`. However, transforming points is slower. Consider converting to a * combine than `MatBase<T, 3, 3>`. However, transforming points is slower. Consider converting to
* rotation matrix if you are rotating many points. * a rotation matrix if you are rotating many points.
* *
* See this for more information: * See this for more information:
* https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Performance_comparisons * https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Performance_comparisons
*/ */
template<typename T = float> struct Quaternion { template<typename T> struct QuaternionBase {
T w, x, y, z; T w, x, y, z;
Quaternion() = default; QuaternionBase() = default;
Quaternion(const T &new_w, const T &new_x, const T &new_y, const T &new_z) QuaternionBase(const T &new_w, const T &new_x, const T &new_y, const T &new_z)
: w(new_w), x(new_x), y(new_y), z(new_z){}; : w(new_w), x(new_x), y(new_y), z(new_z){};
/** /**
* Creates a quaternion from an vector without reordering the components. * Creates a quaternion from an vector without reordering the components.
* \note Component order must follow the scalar constructor (w, x, y, z). * \note Component order must follow the scalar constructor (w, x, y, z).
*/ */
explicit Quaternion(const VecBase<T, 4> &vec) : Quaternion(UNPACK4(vec)){}; explicit QuaternionBase(const VecBase<T, 4> &vec) : QuaternionBase(UNPACK4(vec)){};
/** /**
* Creates a quaternion from real (w) and imaginary parts (x, y, z). * Creates a quaternion from real (w) and imaginary parts (x, y, z).
*/ */
Quaternion(const T &real, const VecBase<T, 3> &imaginary) QuaternionBase(const T &real, const VecBase<T, 3> &imaginary)
: Quaternion(real, UNPACK3(imaginary)){}; : QuaternionBase(real, UNPACK3(imaginary)){};
/** Static functions. */ /** Static functions. */
static Quaternion identity() static QuaternionBase identity()
{ {
return {1, 0, 0, 0}; return {1, 0, 0, 0};
} }
/** This is just for convenience. Does not represent a rotation as it is degenerate. */ /** This is just for convenience. Does not represent a rotation as it is degenerate. */
static Quaternion zero() static QuaternionBase zero()
{ {
return {0, 0, 0, 0}; return {0, 0, 0, 0};
} }
@ -68,7 +66,7 @@ template<typename T = float> struct Quaternion {
* Create a quaternion from an exponential map representation. * Create a quaternion from an exponential map representation.
* An exponential map is basically the rotation axis multiplied by the rotation angle. * An exponential map is basically the rotation axis multiplied by the rotation angle.
*/ */
static Quaternion expmap(const VecBase<T, 3> &expmap); static QuaternionBase expmap(const VecBase<T, 3> &expmap);
/** Conversions. */ /** Conversions. */
@ -87,20 +85,20 @@ template<typename T = float> struct Quaternion {
* Returns the full twist angle for a given \a axis direction. * Returns the full twist angle for a given \a axis direction.
* The twist is the isolated rotation in the plane whose \a axis is normal to. * The twist is the isolated rotation in the plane whose \a axis is normal to.
*/ */
AngleRadian<T> twist_angle(const Axis axis) const; AngleRadianBase<T> twist_angle(const Axis axis) const;
/** /**
* Returns the twist part of this quaternion for the \a axis direction. * Returns the twist part of this quaternion for the \a axis direction.
* The twist is the isolated rotation in the plane whose \a axis is normal to. * The twist is the isolated rotation in the plane whose \a axis is normal to.
*/ */
Quaternion twist(const Axis axis) const; QuaternionBase twist(const Axis axis) const;
/** /**
* Returns the swing part of this quaternion for the basis \a axis direction. * Returns the swing part of this quaternion for the basis \a axis direction.
* The swing is the original quaternion minus the twist around \a axis. * The swing is the original quaternion minus the twist around \a axis.
* So we have the following identity : `q = q.swing(axis) * q.twist(axis)` * So we have the following identity : `q = q.swing(axis) * q.twist(axis)`
*/ */
Quaternion swing(const Axis axis) const; QuaternionBase swing(const Axis axis) const;
/** /**
* Returns the imaginary part of this quaternion (x, y, z). * Returns the imaginary part of this quaternion (x, y, z).
@ -125,11 +123,11 @@ template<typename T = float> struct Quaternion {
* \note This quaternion is expected to be a unit quaternion. * \note This quaternion is expected to be a unit quaternion.
* \note Works even if \a reference is *not* a unit quaternion. * \note Works even if \a reference is *not* a unit quaternion.
*/ */
Quaternion wrapped_around(const Quaternion &reference) const; QuaternionBase wrapped_around(const QuaternionBase &reference) const;
/** Operators. */ /** Operators. */
friend Quaternion operator*(const Quaternion &a, const Quaternion &b) friend QuaternionBase operator*(const QuaternionBase &a, const QuaternionBase &b)
{ {
return {a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z, return {a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
@ -137,58 +135,54 @@ template<typename T = float> struct Quaternion {
a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x}; a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x};
} }
Quaternion &operator*=(const Quaternion &b) QuaternionBase &operator*=(const QuaternionBase &b)
{ {
*this = *this * b; *this = *this * b;
return *this; return *this;
} }
/* Scalar product. */ /* Scalar product. */
friend Quaternion operator*(const Quaternion &a, const T &b) friend QuaternionBase operator*(const QuaternionBase &a, const T &b)
{ {
return {a.w * b, a.x * b, a.y * b, a.z * b}; return {a.w * b, a.x * b, a.y * b, a.z * b};
} }
/* Negate the quaternion. */ /* Negate the quaternion. */
friend Quaternion operator-(const Quaternion &a) friend QuaternionBase operator-(const QuaternionBase &a)
{ {
return {-a.w, -a.x, -a.y, -a.z}; return {-a.w, -a.x, -a.y, -a.z};
} }
friend bool operator==(const Quaternion &a, const Quaternion &b) friend bool operator==(const QuaternionBase &a, const QuaternionBase &b)
{ {
return (a.w == b.w) && (a.x == b.x) && (a.y == b.y) && (a.z == b.z); return (a.w == b.w) && (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
} }
friend std::ostream &operator<<(std::ostream &stream, const Quaternion &rot) friend std::ostream &operator<<(std::ostream &stream, const QuaternionBase &rot)
{ {
return stream << "Quaternion" << static_cast<VecBase<T, 4>>(rot); return stream << "Quaternion" << static_cast<VecBase<T, 4>>(rot);
} }
}; };
} // namespace detail
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Dual-Quaternion /** \name Dual-Quaternion
* \{ */ * \{ */
namespace detail {
/** /**
* A `blender::math::DualQuaternion<T>` implements dual-quaternion skinning with scale aware * A `blender::math::DualQuaternionBase<T>` implements dual-quaternion skinning with scale aware
* transformation. It allows volume preserving deformation for skinning. * transformation. It allows volume preserving deformation for skinning.
* *
* The type is implemented so that multiple weighted `blender::math::DualQuaternion<T>` * The type is implemented so that multiple weighted `blender::math::DualQuaternionBase<T>`
* can be aggregated into a final rotation. Calling `normalize(dual_quat)` is mandatory before * can be aggregated into a final rotation. Calling `normalize(dual_quat)` is mandatory before
* trying to transform points with it. * trying to transform points with it.
*/ */
template<typename T = float> struct DualQuaternion { template<typename T> struct DualQuaternionBase {
/** Non-dual part. */ /** Non-dual part. */
Quaternion<T> quat; QuaternionBase<T> quat;
/** Dual part. */ /** Dual part. */
Quaternion<T> trans; QuaternionBase<T> trans;
/** /**
* Scaling is saved separately to handle cases of non orthonormal axes, non uniform scale and * Scaling is saved separately to handle cases of non orthonormal axes, non uniform scale and
@ -198,7 +192,7 @@ template<typename T = float> struct DualQuaternion {
* It currently holds some translation in some cases. Is this wanted? * It currently holds some translation in some cases. Is this wanted?
* This would save some flops all along the way. */ * This would save some flops all along the way. */
MatBase<T, 4, 4> scale; MatBase<T, 4, 4> scale;
/** The weight of #DualQuaternion.scale. Set to 0 if uniformly scaled to skip `scale` sum. */ /** The weight of #DualQuaternionBase.scale. Set to 0 if uniformly scaled to skip `scale` sum. */
T scale_weight; T scale_weight;
/** /**
* The weight of this dual-quaternion. Used for and summation & normalizing. * The weight of this dual-quaternion. Used for and summation & normalizing.
@ -206,25 +200,25 @@ template<typename T = float> struct DualQuaternion {
*/ */
T quat_weight; T quat_weight;
DualQuaternion() = delete; DualQuaternionBase() = delete;
/** /**
* Dual quaternion without scaling. * Dual quaternion without scaling.
*/ */
DualQuaternion(const Quaternion<T> &non_dual, const Quaternion<T> &dual); DualQuaternionBase(const QuaternionBase<T> &non_dual, const QuaternionBase<T> &dual);
/** /**
* Dual quaternion with scaling. * Dual quaternion with scaling.
*/ */
DualQuaternion(const Quaternion<T> &non_dual, DualQuaternionBase(const QuaternionBase<T> &non_dual,
const Quaternion<T> &dual, const QuaternionBase<T> &dual,
const MatBase<T, 4, 4> &scale_mat); const MatBase<T, 4, 4> &scale_mat);
/** Static functions. */ /** Static functions. */
static DualQuaternion identity() static DualQuaternionBase identity()
{ {
return DualQuaternion(Quaternion<T>::identity(), Quaternion<T>::zero()); return DualQuaternionBase(QuaternionBase<T>::identity(), QuaternionBase<T>::zero());
} }
/** Methods. */ /** Methods. */
@ -232,42 +226,42 @@ template<typename T = float> struct DualQuaternion {
/** Operators. */ /** Operators. */
/** Apply a scalar weight to a dual quaternion. */ /** Apply a scalar weight to a dual quaternion. */
DualQuaternion &operator*=(const T &t); DualQuaternionBase &operator*=(const T &t);
/** Add two weighted dual-quaternions rotations. */ /** Add two weighted dual-quaternions rotations. */
DualQuaternion &operator+=(const DualQuaternion &b); DualQuaternionBase &operator+=(const DualQuaternionBase &b);
/** Apply a scalar weight to a dual quaternion. */ /** Apply a scalar weight to a dual quaternion. */
friend DualQuaternion operator*(const DualQuaternion &a, const T &t) friend DualQuaternionBase operator*(const DualQuaternionBase &a, const T &t)
{ {
DualQuaternion dq = a; DualQuaternionBase dq = a;
dq *= t; dq *= t;
return dq; return dq;
} }
/** Apply a scalar weight to a dual quaternion. */ /** Apply a scalar weight to a dual quaternion. */
friend DualQuaternion operator*(const T &t, const DualQuaternion &a) friend DualQuaternionBase operator*(const T &t, const DualQuaternionBase &a)
{ {
DualQuaternion dq = a; DualQuaternionBase dq = a;
dq *= t; dq *= t;
return dq; return dq;
} }
/** Add two weighted dual-quaternions rotations. */ /** Add two weighted dual-quaternions rotations. */
friend DualQuaternion operator+(const DualQuaternion &a, const DualQuaternion &b) friend DualQuaternionBase operator+(const DualQuaternionBase &a, const DualQuaternionBase &b)
{ {
DualQuaternion dq = a; DualQuaternionBase dq = a;
dq += b; dq += b;
return dq; return dq;
} }
friend bool operator==(const DualQuaternion &a, const DualQuaternion &b) friend bool operator==(const DualQuaternionBase &a, const DualQuaternionBase &b)
{ {
return (a.quat == b.quat) && (a.trans == b.trans) && (a.quat_weight == b.quat_weight) && return (a.quat == b.quat) && (a.trans == b.trans) && (a.quat_weight == b.quat_weight) &&
(a.scale_weight == b.scale_weight) && (a.scale == b.scale); (a.scale_weight == b.scale_weight) && (a.scale == b.scale);
} }
friend std::ostream &operator<<(std::ostream &stream, const DualQuaternion &rot) friend std::ostream &operator<<(std::ostream &stream, const DualQuaternionBase &rot)
{ {
stream << "DualQuaternion(\n"; stream << "DualQuaternion(\n";
stream << " .quat = " << rot.quat << "\n"; stream << " .quat = " << rot.quat << "\n";
@ -281,20 +275,18 @@ template<typename T = float> struct DualQuaternion {
} }
}; };
} // namespace detail
/** /**
* Returns true if the #DualQuaternion has not been mixed with other #DualQuaternion and needs no * Returns true if the #DualQuaternion has not been mixed with other #DualQuaternion and needs no
* normalization. * normalization.
*/ */
template<typename T> [[nodiscard]] inline bool is_normalized(const detail::DualQuaternion<T> &dq) template<typename T> [[nodiscard]] inline bool is_normalized(const DualQuaternionBase<T> &dq)
{ {
return dq.quat_weight == T(1); return dq.quat_weight == T(1);
} }
/** \} */ /** \} */
template<typename U> struct AssertUnitEpsilon<detail::Quaternion<U>> { template<typename U> struct AssertUnitEpsilon<QuaternionBase<U>> {
static constexpr U value = AssertUnitEpsilon<U>::value * 10; static constexpr U value = AssertUnitEpsilon<U>::value * 10;
}; };
@ -310,8 +302,8 @@ template<> struct TypeTraits<float> {
using DoublePrecision = double; using DoublePrecision = double;
}; };
using Quaternion = math::detail::Quaternion<float>; using Quaternion = QuaternionBase<float>;
using DualQuaternion = math::detail::DualQuaternion<float>; using DualQuaternion = DualQuaternionBase<float>;
} // namespace blender::math } // namespace blender::math

View File

@ -27,7 +27,7 @@ namespace blender::math {
* This might introduce some precision loss and have performance implication. * This might introduce some precision loss and have performance implication.
*/ */
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::Quaternion<T> rotate(const detail::Quaternion<T> &a, const RotT &b); [[nodiscard]] QuaternionBase<T> rotate(const QuaternionBase<T> &a, const RotT &b);
/** /**
* Rotate \a a by \a b. In other word, insert the \a b rotation before \a a. * Rotate \a a by \a b. In other word, insert the \a b rotation before \a a.
@ -36,8 +36,7 @@ template<typename T, typename RotT>
* This might introduce some precision loss and have performance implication. * This might introduce some precision loss and have performance implication.
*/ */
template<typename T, typename RotT, typename AngleT> template<typename T, typename RotT, typename AngleT>
[[nodiscard]] detail::AxisAngle<T, AngleT> rotate(const detail::AxisAngle<T, AngleT> &a, [[nodiscard]] AxisAngleBase<T, AngleT> rotate(const AxisAngleBase<T, AngleT> &a, const RotT &b);
const RotT &b);
/** /**
* Rotate \a a by \a b. In other word, insert the \a b rotation before \a a. * Rotate \a a by \a b. In other word, insert the \a b rotation before \a a.
@ -46,7 +45,7 @@ template<typename T, typename RotT, typename AngleT>
* This might introduce some precision loss and have performance implication. * This might introduce some precision loss and have performance implication.
*/ */
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::EulerXYZ<T> rotate(const detail::EulerXYZ<T> &a, const RotT &b); [[nodiscard]] EulerXYZBase<T> rotate(const EulerXYZBase<T> &a, const RotT &b);
/** /**
* Rotate \a a by \a b. In other word, insert the \a b rotation before \a a. * Rotate \a a by \a b. In other word, insert the \a b rotation before \a a.
@ -55,14 +54,14 @@ template<typename T, typename RotT>
* This might introduce some precision loss and have performance implication. * This might introduce some precision loss and have performance implication.
*/ */
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::Euler3<T> rotate(const detail::Euler3<T> &a, const RotT &b); [[nodiscard]] Euler3Base<T> rotate(const Euler3Base<T> &a, const RotT &b);
/** /**
* Return rotation from orientation \a a to orientation \a b into another quaternion. * Return rotation from orientation \a a to orientation \a b into another quaternion.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> rotation_between(const detail::Quaternion<T> &a, [[nodiscard]] QuaternionBase<T> rotation_between(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b); const QuaternionBase<T> &b);
/** /**
* Create a orientation from a triangle plane and the axis formed by the segment(v1, v2). * Create a orientation from a triangle plane and the axis formed by the segment(v1, v2).
@ -70,18 +69,18 @@ template<typename T>
* Used for Ngons when their normal is known. * Used for Ngons when their normal is known.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_triangle(const VecBase<T, 3> &v1, [[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
const VecBase<T, 3> &v2, const VecBase<T, 3> &v2,
const VecBase<T, 3> &v3, const VecBase<T, 3> &v3,
const VecBase<T, 3> &normal); const VecBase<T, 3> &normal);
/** /**
* Create a orientation from a triangle plane and the axis formed by the segment(v1, v2). * Create a orientation from a triangle plane and the axis formed by the segment(v1, v2).
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_triangle(const VecBase<T, 3> &v1, [[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
const VecBase<T, 3> &v2, const VecBase<T, 3> &v2,
const VecBase<T, 3> &v3); const VecBase<T, 3> &v3);
/** /**
* Create a rotation from a vector and a basis rotation. * Create a rotation from a vector and a basis rotation.
@ -90,22 +89,21 @@ template<typename T>
* \a up_flag is supposed to be #Object.upflag * \a up_flag is supposed to be #Object.upflag
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_vector(const VecBase<T, 3> &vector, [[nodiscard]] QuaternionBase<T> from_vector(const VecBase<T, 3> &vector,
const AxisSigned track_flag, const AxisSigned track_flag,
const Axis up_flag); const Axis up_flag);
/** /**
* Returns a quaternion for converting local space to tracking space. * Returns a quaternion for converting local space to tracking space.
* This is slightly different than from_axis_conversion for legacy reasons. * This is slightly different than from_axis_conversion for legacy reasons.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_tracking(AxisSigned forward_axis, Axis up_axis); [[nodiscard]] QuaternionBase<T> from_tracking(AxisSigned forward_axis, Axis up_axis);
/** /**
* Convert euler rotation to gimbal rotation matrix. * Convert euler rotation to gimbal rotation matrix.
*/ */
template<typename T> template<typename T> [[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const Euler3Base<T> &rotation);
[[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const detail::Euler3<T> &rotation);
/** \} */ /** \} */
@ -120,7 +118,7 @@ template<typename T>
* Unlike the angle between vectors, this does *NOT* return the shortest angle. * Unlike the angle between vectors, this does *NOT* return the shortest angle.
* See `angle_of_signed` below for this. * See `angle_of_signed` below for this.
*/ */
template<typename T> [[nodiscard]] detail::AngleRadian<T> angle_of(const detail::Quaternion<T> &q) template<typename T> [[nodiscard]] AngleRadianBase<T> angle_of(const QuaternionBase<T> &q)
{ {
BLI_assert(is_unit_scale(q)); BLI_assert(is_unit_scale(q));
return T(2) * math::safe_acos(q.w); return T(2) * math::safe_acos(q.w);
@ -134,8 +132,7 @@ template<typename T> [[nodiscard]] detail::AngleRadian<T> angle_of(const detail:
* allows to use 'abs(angle_of_signed(...))' to get the shortest angle between quaternions with * allows to use 'abs(angle_of_signed(...))' to get the shortest angle between quaternions with
* higher precision than subtracting 2pi afterwards. * higher precision than subtracting 2pi afterwards.
*/ */
template<typename T> template<typename T> [[nodiscard]] AngleRadianBase<T> angle_of_signed(const QuaternionBase<T> &q)
[[nodiscard]] detail::AngleRadian<T> angle_of_signed(const detail::Quaternion<T> &q)
{ {
BLI_assert(is_unit_scale(q)); BLI_assert(is_unit_scale(q));
return T(2) * ((q.w >= T(0)) ? math::safe_acos(q.w) : -math::safe_acos(-q.w)); return T(2) * ((q.w >= T(0)) ? math::safe_acos(q.w) : -math::safe_acos(-q.w));
@ -148,13 +145,13 @@ template<typename T>
* See `angle_of` for more detail. * See `angle_of` for more detail.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::AngleRadian<T> angle_between(const detail::Quaternion<T> &a, [[nodiscard]] AngleRadianBase<T> angle_between(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b) const QuaternionBase<T> &b)
{ {
return angle_of(rotation_between(a, b)); return angle_of(rotation_between(a, b));
} }
template<typename T> template<typename T>
[[nodiscard]] detail::AngleRadian<T> angle_between(const VecBase<T, 3> &a, const VecBase<T, 3> &b) [[nodiscard]] AngleRadianBase<T> angle_between(const VecBase<T, 3> &a, const VecBase<T, 3> &b)
{ {
BLI_assert(is_unit_scale(a)); BLI_assert(is_unit_scale(a));
BLI_assert(is_unit_scale(b)); BLI_assert(is_unit_scale(b));
@ -178,8 +175,8 @@ template<typename T>
* See `angle_of_signed` for more detail. * See `angle_of_signed` for more detail.
*/ */
template<typename T> template<typename T>
[[nodiscard]] detail::AngleRadian<T> angle_between_signed(const detail::Quaternion<T> &a, [[nodiscard]] AngleRadianBase<T> angle_between_signed(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b) const QuaternionBase<T> &b)
{ {
return angle_of_signed(rotation_between(a, b)); return angle_of_signed(rotation_between(a, b));
} }
@ -191,27 +188,26 @@ template<typename T>
* \{ */ * \{ */
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::Quaternion<T> rotate(const detail::Quaternion<T> &a, const RotT &b) [[nodiscard]] QuaternionBase<T> rotate(const QuaternionBase<T> &a, const RotT &b)
{ {
return a * detail::Quaternion<T>(b); return a * QuaternionBase<T>(b);
} }
template<typename T, typename RotT, typename AngleT> template<typename T, typename RotT, typename AngleT>
[[nodiscard]] detail::AxisAngle<T, AngleT> rotate(const detail::AxisAngle<T, AngleT> &a, [[nodiscard]] AxisAngleBase<T, AngleT> rotate(const AxisAngleBase<T, AngleT> &a, const RotT &b)
const RotT &b)
{ {
return detail::AxisAngle<T, AngleT>(detail::Quaternion<T>(a) * detail::Quaternion<T>(b)); return AxisAngleBase<T, AngleT>(QuaternionBase<T>(a) * QuaternionBase<T>(b));
} }
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::EulerXYZ<T> rotate(const detail::EulerXYZ<T> &a, const RotT &b) [[nodiscard]] EulerXYZBase<T> rotate(const EulerXYZBase<T> &a, const RotT &b)
{ {
MatBase<T, 3, 3> tmp = from_rotation<MatBase<T, 3, 3>>(a) * from_rotation<MatBase<T, 3, 3>>(b); MatBase<T, 3, 3> tmp = from_rotation<MatBase<T, 3, 3>>(a) * from_rotation<MatBase<T, 3, 3>>(b);
return to_euler(tmp); return to_euler(tmp);
} }
template<typename T, typename RotT> template<typename T, typename RotT>
[[nodiscard]] detail::Euler3<T> rotate(const detail::Euler3<T> &a, const RotT &b) [[nodiscard]] Euler3Base<T> rotate(const Euler3Base<T> &a, const RotT &b)
{ {
const MatBase<T, 3, 3> tmp = from_rotation<MatBase<T, 3, 3>>(a) * const MatBase<T, 3, 3> tmp = from_rotation<MatBase<T, 3, 3>>(a) *
from_rotation<MatBase<T, 3, 3>>(b); from_rotation<MatBase<T, 3, 3>>(b);
@ -219,17 +215,17 @@ template<typename T, typename RotT>
} }
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> rotation_between(const detail::Quaternion<T> &a, [[nodiscard]] QuaternionBase<T> rotation_between(const QuaternionBase<T> &a,
const detail::Quaternion<T> &b) const QuaternionBase<T> &b)
{ {
return invert(a) * b; return invert(a) * b;
} }
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_triangle(const VecBase<T, 3> &v1, [[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
const VecBase<T, 3> &v2, const VecBase<T, 3> &v2,
const VecBase<T, 3> &v3, const VecBase<T, 3> &v3,
const VecBase<T, 3> &normal) const VecBase<T, 3> &normal)
{ {
/* Force to used an unused var to avoid the same function signature as the version without /* Force to used an unused var to avoid the same function signature as the version without
* `normal` argument. */ * `normal` argument. */
@ -246,7 +242,7 @@ template<typename T>
T angle = T(-0.5) * math::safe_acos(z_axis.z); T angle = T(-0.5) * math::safe_acos(z_axis.z);
T si = math::sin(angle); T si = math::sin(angle);
detail::Quaternion<T> q1(math::cos(angle), nor.x * si, nor.y * si, T(0)); QuaternionBase<T> q1(math::cos(angle), nor.x * si, nor.y * si, T(0));
/* Rotate back line v1-v2. */ /* Rotate back line v1-v2. */
Vec3T line = transform_point(conjugate(q1), (v2 - v1)); Vec3T line = transform_point(conjugate(q1), (v2 - v1));
@ -254,23 +250,23 @@ template<typename T>
line = normalize(Vec3T(line.x, line.y, T(0))); line = normalize(Vec3T(line.x, line.y, T(0)));
angle = T(0.5) * math::atan2(line.y, line.x); angle = T(0.5) * math::atan2(line.y, line.x);
detail::Quaternion<T> q2(math::cos(angle), 0.0, 0.0, math::sin(angle)); QuaternionBase<T> q2(math::cos(angle), 0.0, 0.0, math::sin(angle));
return q1 * q2; return q1 * q2;
} }
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_triangle(const VecBase<T, 3> &v1, [[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
const VecBase<T, 3> &v2, const VecBase<T, 3> &v2,
const VecBase<T, 3> &v3) const VecBase<T, 3> &v3)
{ {
return from_triangle(v1, v2, v3, normal_tri(v1, v2, v3)); return from_triangle(v1, v2, v3, normal_tri(v1, v2, v3));
} }
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_vector(const VecBase<T, 3> &vector, [[nodiscard]] QuaternionBase<T> from_vector(const VecBase<T, 3> &vector,
const AxisSigned track_flag, const AxisSigned track_flag,
const Axis up_flag) const Axis up_flag)
{ {
using Vec2T = VecBase<T, 2>; using Vec2T = VecBase<T, 2>;
using Vec3T = VecBase<T, 3>; using Vec3T = VecBase<T, 3>;
@ -279,7 +275,7 @@ template<typename T>
const T vec_len = length(vector); const T vec_len = length(vector);
if (UNLIKELY(vec_len == 0.0f)) { if (UNLIKELY(vec_len == 0.0f)) {
return detail::Quaternion<T>::identity(); return QuaternionBase<T>::identity();
} }
const Axis axis = track_flag.axis(); const Axis axis = track_flag.axis();
@ -313,8 +309,8 @@ template<typename T>
* Avoiding the need for safe_acos and deriving sin from cos. */ * Avoiding the need for safe_acos and deriving sin from cos. */
const T rotation_angle = math::safe_acos(vec[axis.as_int()] / vec_len); const T rotation_angle = math::safe_acos(vec[axis.as_int()] / vec_len);
const detail::Quaternion<T> q1 = to_quaternion( const QuaternionBase<T> q1 = to_quaternion(
detail::AxisAngle<T, detail::AngleRadian<T>>(rotation_axis, rotation_angle)); AxisAngleBase<T, AngleRadianBase<T>>(rotation_axis, rotation_angle));
if (axis == up_flag) { if (axis == up_flag) {
/* Nothing else to do. */ /* Nothing else to do. */
@ -341,27 +337,26 @@ template<typename T>
projected = -projected; projected = -projected;
} }
const detail::AngleCartesian<T> angle(projected.x, projected.y); const AngleCartesianBase<T> angle(projected.x, projected.y);
const detail::AngleCartesian<T> half_angle = angle / T(2); const AngleCartesianBase<T> half_angle = angle / T(2);
const detail::Quaternion<T> q2(Vec4T(half_angle.cos(), vec * (half_angle.sin() / vec_len))); const QuaternionBase<T> q2(Vec4T(half_angle.cos(), vec * (half_angle.sin() / vec_len)));
return q2 * q1; return q2 * q1;
} }
template<typename T> template<typename T>
[[nodiscard]] detail::Quaternion<T> from_tracking(AxisSigned forward_axis, Axis up_axis) [[nodiscard]] QuaternionBase<T> from_tracking(AxisSigned forward_axis, Axis up_axis)
{ {
BLI_assert(forward_axis.axis() != up_axis); BLI_assert(forward_axis.axis() != up_axis);
/* Curve have Z forward, Y up, X left. */ /* Curve have Z forward, Y up, X left. */
return detail::Quaternion<T>( return QuaternionBase<T>(
rotation_between(from_orthonormal_axes(AxisSigned::Z_POS, AxisSigned::Y_POS), rotation_between(from_orthonormal_axes(AxisSigned::Z_POS, AxisSigned::Y_POS),
from_orthonormal_axes(forward_axis, AxisSigned(up_axis)))); from_orthonormal_axes(forward_axis, AxisSigned(up_axis))));
} }
template<typename T> template<typename T> [[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const Euler3Base<T> &rotation)
[[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const detail::Euler3<T> &rotation)
{ {
using Mat3T = MatBase<T, 3, 3>; using Mat3T = MatBase<T, 3, 3>;
using Vec3T = VecBase<T, 3>; using Vec3T = VecBase<T, 3>;
@ -373,7 +368,7 @@ template<typename T>
/* First axis is local. */ /* First axis is local. */
result[i_index] = from_rotation<Mat3T>(rotation)[i_index]; result[i_index] = from_rotation<Mat3T>(rotation)[i_index];
/* Second axis is local minus first rotation. */ /* Second axis is local minus first rotation. */
detail::Euler3<T> tmp_rot = rotation; Euler3Base<T> tmp_rot = rotation;
tmp_rot.i() = T(0); tmp_rot.i() = T(0);
result[j_index] = from_rotation<Mat3T>(tmp_rot)[j_index]; result[j_index] = from_rotation<Mat3T>(tmp_rot)[j_index];
/* Last axis is global. */ /* Last axis is global. */
@ -393,7 +388,7 @@ template<typename T>
* Creates a quaternion from an axis triple. * Creates a quaternion from an axis triple.
* This is faster and more precise than converting from another representation. * This is faster and more precise than converting from another representation.
*/ */
template<typename T> detail::Quaternion<T> to_quaternion(const CartesianBasis &rotation) template<typename T> QuaternionBase<T> to_quaternion(const CartesianBasis &rotation)
{ {
/** /**
* There are only 6 * 4 = 24 possible valid orthonormal orientations. * There are only 6 * 4 = 24 possible valid orthonormal orientations.
@ -406,53 +401,53 @@ template<typename T> detail::Quaternion<T> to_quaternion(const CartesianBasis &r
switch (map(rotation.axes.x, rotation.axes.y, rotation.axes.z)) { switch (map(rotation.axes.x, rotation.axes.y, rotation.axes.z)) {
default: default:
return detail::Quaternion<T>::identity(); return QuaternionBase<T>::identity();
case map(AxisSigned::Z_POS, AxisSigned::X_POS, AxisSigned::Y_POS): case map(AxisSigned::Z_POS, AxisSigned::X_POS, AxisSigned::Y_POS):
return detail::Quaternion<T>{T(0.5), T(-0.5), T(-0.5), T(-0.5)}; return QuaternionBase<T>{T(0.5), T(-0.5), T(-0.5), T(-0.5)};
case map(AxisSigned::Y_NEG, AxisSigned::X_POS, AxisSigned::Z_POS): case map(AxisSigned::Y_NEG, AxisSigned::X_POS, AxisSigned::Z_POS):
return detail::Quaternion<T>{T(M_SQRT1_2), T(0), T(0), T(-M_SQRT1_2)}; return QuaternionBase<T>{T(M_SQRT1_2), T(0), T(0), T(-M_SQRT1_2)};
case map(AxisSigned::Z_NEG, AxisSigned::X_POS, AxisSigned::Y_NEG): case map(AxisSigned::Z_NEG, AxisSigned::X_POS, AxisSigned::Y_NEG):
return detail::Quaternion<T>{T(0.5), T(0.5), T(0.5), T(-0.5)}; return QuaternionBase<T>{T(0.5), T(0.5), T(0.5), T(-0.5)};
case map(AxisSigned::Y_POS, AxisSigned::X_POS, AxisSigned::Z_NEG): case map(AxisSigned::Y_POS, AxisSigned::X_POS, AxisSigned::Z_NEG):
return detail::Quaternion<T>{T(0), T(M_SQRT1_2), T(M_SQRT1_2), T(0)}; return QuaternionBase<T>{T(0), T(M_SQRT1_2), T(M_SQRT1_2), T(0)};
case map(AxisSigned::Z_NEG, AxisSigned::Y_POS, AxisSigned::X_POS): case map(AxisSigned::Z_NEG, AxisSigned::Y_POS, AxisSigned::X_POS):
return detail::Quaternion<T>{T(M_SQRT1_2), T(0), T(M_SQRT1_2), T(0)}; return QuaternionBase<T>{T(M_SQRT1_2), T(0), T(M_SQRT1_2), T(0)};
case map(AxisSigned::Z_POS, AxisSigned::Y_POS, AxisSigned::X_NEG): case map(AxisSigned::Z_POS, AxisSigned::Y_POS, AxisSigned::X_NEG):
return detail::Quaternion<T>{T(M_SQRT1_2), T(0), T(-M_SQRT1_2), T(0)}; return QuaternionBase<T>{T(M_SQRT1_2), T(0), T(-M_SQRT1_2), T(0)};
case map(AxisSigned::X_NEG, AxisSigned::Y_POS, AxisSigned::Z_NEG): case map(AxisSigned::X_NEG, AxisSigned::Y_POS, AxisSigned::Z_NEG):
return detail::Quaternion<T>{T(0), T(0), T(1), T(0)}; return QuaternionBase<T>{T(0), T(0), T(1), T(0)};
case map(AxisSigned::Y_POS, AxisSigned::Z_POS, AxisSigned::X_POS): case map(AxisSigned::Y_POS, AxisSigned::Z_POS, AxisSigned::X_POS):
return detail::Quaternion<T>{T(0.5), T(0.5), T(0.5), T(0.5)}; return QuaternionBase<T>{T(0.5), T(0.5), T(0.5), T(0.5)};
case map(AxisSigned::X_NEG, AxisSigned::Z_POS, AxisSigned::Y_POS): case map(AxisSigned::X_NEG, AxisSigned::Z_POS, AxisSigned::Y_POS):
return detail::Quaternion<T>{T(0), T(0), T(M_SQRT1_2), T(M_SQRT1_2)}; return QuaternionBase<T>{T(0), T(0), T(M_SQRT1_2), T(M_SQRT1_2)};
case map(AxisSigned::Y_NEG, AxisSigned::Z_POS, AxisSigned::X_NEG): case map(AxisSigned::Y_NEG, AxisSigned::Z_POS, AxisSigned::X_NEG):
return detail::Quaternion<T>{T(0.5), T(0.5), T(-0.5), T(-0.5)}; return QuaternionBase<T>{T(0.5), T(0.5), T(-0.5), T(-0.5)};
case map(AxisSigned::X_POS, AxisSigned::Z_POS, AxisSigned::Y_NEG): case map(AxisSigned::X_POS, AxisSigned::Z_POS, AxisSigned::Y_NEG):
return detail::Quaternion<T>{T(M_SQRT1_2), T(M_SQRT1_2), T(0), T(0)}; return QuaternionBase<T>{T(M_SQRT1_2), T(M_SQRT1_2), T(0), T(0)};
case map(AxisSigned::Z_NEG, AxisSigned::X_NEG, AxisSigned::Y_POS): case map(AxisSigned::Z_NEG, AxisSigned::X_NEG, AxisSigned::Y_POS):
return detail::Quaternion<T>{T(0.5), T(-0.5), T(0.5), T(0.5)}; return QuaternionBase<T>{T(0.5), T(-0.5), T(0.5), T(0.5)};
case map(AxisSigned::Y_POS, AxisSigned::X_NEG, AxisSigned::Z_POS): case map(AxisSigned::Y_POS, AxisSigned::X_NEG, AxisSigned::Z_POS):
return detail::Quaternion<T>{T(M_SQRT1_2), T(0), T(0), T(M_SQRT1_2)}; return QuaternionBase<T>{T(M_SQRT1_2), T(0), T(0), T(M_SQRT1_2)};
case map(AxisSigned::Z_POS, AxisSigned::X_NEG, AxisSigned::Y_NEG): case map(AxisSigned::Z_POS, AxisSigned::X_NEG, AxisSigned::Y_NEG):
return detail::Quaternion<T>{T(0.5), T(0.5), T(-0.5), T(0.5)}; return QuaternionBase<T>{T(0.5), T(0.5), T(-0.5), T(0.5)};
case map(AxisSigned::Y_NEG, AxisSigned::X_NEG, AxisSigned::Z_NEG): case map(AxisSigned::Y_NEG, AxisSigned::X_NEG, AxisSigned::Z_NEG):
return detail::Quaternion<T>{T(0), T(-M_SQRT1_2), T(M_SQRT1_2), T(0)}; return QuaternionBase<T>{T(0), T(-M_SQRT1_2), T(M_SQRT1_2), T(0)};
case map(AxisSigned::Z_POS, AxisSigned::Y_NEG, AxisSigned::X_POS): case map(AxisSigned::Z_POS, AxisSigned::Y_NEG, AxisSigned::X_POS):
return detail::Quaternion<T>{T(0), T(M_SQRT1_2), T(0), T(M_SQRT1_2)}; return QuaternionBase<T>{T(0), T(M_SQRT1_2), T(0), T(M_SQRT1_2)};
case map(AxisSigned::X_NEG, AxisSigned::Y_NEG, AxisSigned::Z_POS): case map(AxisSigned::X_NEG, AxisSigned::Y_NEG, AxisSigned::Z_POS):
return detail::Quaternion<T>{T(0), T(0), T(0), T(1)}; return QuaternionBase<T>{T(0), T(0), T(0), T(1)};
case map(AxisSigned::Z_NEG, AxisSigned::Y_NEG, AxisSigned::X_NEG): case map(AxisSigned::Z_NEG, AxisSigned::Y_NEG, AxisSigned::X_NEG):
return detail::Quaternion<T>{T(0), T(-M_SQRT1_2), T(0), T(M_SQRT1_2)}; return QuaternionBase<T>{T(0), T(-M_SQRT1_2), T(0), T(M_SQRT1_2)};
case map(AxisSigned::X_POS, AxisSigned::Y_NEG, AxisSigned::Z_NEG): case map(AxisSigned::X_POS, AxisSigned::Y_NEG, AxisSigned::Z_NEG):
return detail::Quaternion<T>{T(0), T(1), T(0), T(0)}; return QuaternionBase<T>{T(0), T(1), T(0), T(0)};
case map(AxisSigned::Y_NEG, AxisSigned::Z_NEG, AxisSigned::X_POS): case map(AxisSigned::Y_NEG, AxisSigned::Z_NEG, AxisSigned::X_POS):
return detail::Quaternion<T>{T(0.5), T(-0.5), T(0.5), T(-0.5)}; return QuaternionBase<T>{T(0.5), T(-0.5), T(0.5), T(-0.5)};
case map(AxisSigned::X_POS, AxisSigned::Z_NEG, AxisSigned::Y_POS): case map(AxisSigned::X_POS, AxisSigned::Z_NEG, AxisSigned::Y_POS):
return detail::Quaternion<T>{T(M_SQRT1_2), T(-M_SQRT1_2), T(0), T(0)}; return QuaternionBase<T>{T(M_SQRT1_2), T(-M_SQRT1_2), T(0), T(0)};
case map(AxisSigned::Y_POS, AxisSigned::Z_NEG, AxisSigned::X_NEG): case map(AxisSigned::Y_POS, AxisSigned::Z_NEG, AxisSigned::X_NEG):
return detail::Quaternion<T>{T(0.5), T(-0.5), T(-0.5), T(0.5)}; return QuaternionBase<T>{T(0.5), T(-0.5), T(-0.5), T(0.5)};
case map(AxisSigned::X_NEG, AxisSigned::Z_NEG, AxisSigned::Y_NEG): case map(AxisSigned::X_NEG, AxisSigned::Z_NEG, AxisSigned::Y_NEG):
return detail::Quaternion<T>{T(0), T(0), T(-M_SQRT1_2), T(M_SQRT1_2)}; return QuaternionBase<T>{T(0), T(0), T(-M_SQRT1_2), T(M_SQRT1_2)};
} }
} }

View File

@ -339,9 +339,9 @@ MatBase<T, 3, 3> interpolate(const MatBase<T, 3, 3> &A, const MatBase<T, 3, 3> &
P_B = -P_B; P_B = -P_B;
} }
detail::Quaternion<T> quat_A = math::to_quaternion(normalize(U_A)); QuaternionBase<T> quat_A = math::to_quaternion(normalize(U_A));
detail::Quaternion<T> quat_B = math::to_quaternion(normalize(U_B)); QuaternionBase<T> quat_B = math::to_quaternion(normalize(U_B));
detail::Quaternion<T> quat = math::interpolate(quat_A, quat_B, t); QuaternionBase<T> quat = math::interpolate(quat_A, quat_B, t);
Mat3T U = from_rotation<Mat3T>(quat); Mat3T U = from_rotation<Mat3T>(quat);
Mat3T P = interpolate_linear(P_A, P_B, t); Mat3T P = interpolate_linear(P_A, P_B, t);
@ -372,7 +372,7 @@ template double4x4 interpolate(const double4x4 &a, const double4x4 &b, double t)
template<typename T> template<typename T>
MatBase<T, 3, 3> interpolate_fast(const MatBase<T, 3, 3> &a, const MatBase<T, 3, 3> &b, T t) MatBase<T, 3, 3> interpolate_fast(const MatBase<T, 3, 3> &a, const MatBase<T, 3, 3> &b, T t)
{ {
using QuaternionT = detail::Quaternion<T>; using QuaternionT = QuaternionBase<T>;
using Vec3T = typename MatBase<T, 3, 3>::vec3_type; using Vec3T = typename MatBase<T, 3, 3>::vec3_type;
Vec3T a_scale, b_scale; Vec3T a_scale, b_scale;
@ -391,7 +391,7 @@ template double3x3 interpolate_fast(const double3x3 &a, const double3x3 &b, doub
template<typename T> template<typename T>
MatBase<T, 4, 4> interpolate_fast(const MatBase<T, 4, 4> &a, const MatBase<T, 4, 4> &b, T t) MatBase<T, 4, 4> interpolate_fast(const MatBase<T, 4, 4> &a, const MatBase<T, 4, 4> &b, T t)
{ {
using QuaternionT = detail::Quaternion<T>; using QuaternionT = QuaternionBase<T>;
using Vec3T = typename MatBase<T, 3, 3>::vec3_type; using Vec3T = typename MatBase<T, 3, 3>::vec3_type;
Vec3T a_loc, b_loc; Vec3T a_loc, b_loc;
@ -447,30 +447,30 @@ Quaternion to_quaternion_legacy(const float3x3 &mat)
namespace detail { namespace detail {
template void normalized_to_eul2(const float3x3 &mat, template void normalized_to_eul2(const float3x3 &mat,
detail::Euler3<float> &eul1, Euler3Base<float> &eul1,
detail::Euler3<float> &eul2); Euler3Base<float> &eul2);
template void normalized_to_eul2(const float3x3 &mat, template void normalized_to_eul2(const float3x3 &mat,
detail::EulerXYZ<float> &eul1, EulerXYZBase<float> &eul1,
detail::EulerXYZ<float> &eul2); EulerXYZBase<float> &eul2);
template void normalized_to_eul2(const double3x3 &mat, template void normalized_to_eul2(const double3x3 &mat,
detail::EulerXYZ<double> &eul1, EulerXYZBase<double> &eul1,
detail::EulerXYZ<double> &eul2); EulerXYZBase<double> &eul2);
template detail::Quaternion<float> normalized_to_quat_with_checks(const float3x3 &mat); template QuaternionBase<float> normalized_to_quat_with_checks(const float3x3 &mat);
template detail::Quaternion<double> normalized_to_quat_with_checks(const double3x3 &mat); template QuaternionBase<double> normalized_to_quat_with_checks(const double3x3 &mat);
template MatBase<float, 2, 2> from_rotation(const detail::AngleRadian<float> &rotation); template MatBase<float, 2, 2> from_rotation(const AngleRadian &rotation);
template MatBase<float, 3, 3> from_rotation(const detail::AngleRadian<float> &rotation); template MatBase<float, 3, 3> from_rotation(const AngleRadian &rotation);
template MatBase<float, 3, 3> from_rotation(const detail::EulerXYZ<float> &rotation); template MatBase<float, 3, 3> from_rotation(const EulerXYZ &rotation);
template MatBase<float, 4, 4> from_rotation(const detail::EulerXYZ<float> &rotation); template MatBase<float, 4, 4> from_rotation(const EulerXYZ &rotation);
template MatBase<float, 3, 3> from_rotation(const detail::Euler3<float> &rotation); template MatBase<float, 3, 3> from_rotation(const Euler3 &rotation);
template MatBase<float, 4, 4> from_rotation(const detail::Euler3<float> &rotation); template MatBase<float, 4, 4> from_rotation(const Euler3 &rotation);
template MatBase<float, 3, 3> from_rotation(const detail::Quaternion<float> &rotation); template MatBase<float, 3, 3> from_rotation(const Quaternion &rotation);
template MatBase<float, 4, 4> from_rotation(const detail::Quaternion<float> &rotation); template MatBase<float, 4, 4> from_rotation(const Quaternion &rotation);
template MatBase<float, 3, 3> from_rotation(const math::AxisAngle &rotation); template MatBase<float, 3, 3> from_rotation(const AxisAngle &rotation);
template MatBase<float, 4, 4> from_rotation(const math::AxisAngle &rotation); template MatBase<float, 4, 4> from_rotation(const AxisAngle &rotation);
template MatBase<float, 3, 3> from_rotation(const math::AxisAngleCartesian &rotation); template MatBase<float, 3, 3> from_rotation(const AxisAngleCartesian &rotation);
template MatBase<float, 4, 4> from_rotation(const math::AxisAngleCartesian &rotation); template MatBase<float, 4, 4> from_rotation(const AxisAngleCartesian &rotation);
} // namespace detail } // namespace detail

View File

@ -3651,17 +3651,7 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu,
rctf *r_bounds) rctf *r_bounds)
{ {
const bool fcu_selection_only = false; const bool fcu_selection_only = false;
const bool found_bounds = BKE_fcurve_calc_bounds(fcu, BKE_fcurve_calc_bounds(fcu, fcu_selection_only, include_handles, range, r_bounds);
&r_bounds->xmin,
&r_bounds->xmax,
&r_bounds->ymin,
&r_bounds->ymax,
fcu_selection_only,
include_handles,
range);
if (!found_bounds) {
return false;
}
const short mapping_flag = ANIM_get_normalization_flags(ac); const short mapping_flag = ANIM_get_normalization_flags(ac);
@ -3736,8 +3726,8 @@ static void get_view_range(Scene *scene, const bool use_preview_range, float r_r
r_range[1] = scene->r.pefra; r_range[1] = scene->r.pefra;
} }
else { else {
r_range[0] = -FLT_MAX; r_range[0] = scene->r.sfra;
r_range[1] = FLT_MAX; r_range[1] = scene->r.efra;
} }
} }
@ -3879,6 +3869,7 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const
float range[2]; float range[2];
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range"); const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
get_view_range(ac.scene, use_preview_range, range); get_view_range(ac.scene, use_preview_range, range);
rctf bounds; rctf bounds;

View File

@ -203,7 +203,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
float tmin, tmax; float tmin, tmax;
/* get range and apply necessary scaling before processing */ /* get range and apply necessary scaling before processing */
if (BKE_fcurve_calc_range(fcu, &tmin, &tmax, onlySel, false)) { if (BKE_fcurve_calc_range(fcu, &tmin, &tmax, onlySel)) {
if (adt) { if (adt) {
tmin = BKE_nla_tweakedit_remap(adt, tmin, NLATIME_CONVERT_MAP); tmin = BKE_nla_tweakedit_remap(adt, tmin, NLATIME_CONVERT_MAP);

View File

@ -632,7 +632,7 @@ static void draw_fcurve_curve(bAnimContext *ac,
if (!draw_extrapolation) { if (!draw_extrapolation) {
float fcu_start = 0; float fcu_start = 0;
float fcu_end = 0; float fcu_end = 0;
BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false, false); BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false);
fcu_start = BKE_nla_tweakedit_remap(adt, fcu_start, NLATIME_CONVERT_MAP); fcu_start = BKE_nla_tweakedit_remap(adt, fcu_start, NLATIME_CONVERT_MAP);
fcu_end = BKE_nla_tweakedit_remap(adt, fcu_end, NLATIME_CONVERT_MAP); fcu_end = BKE_nla_tweakedit_remap(adt, fcu_end, NLATIME_CONVERT_MAP);

View File

@ -85,40 +85,39 @@ void get_graph_keyframe_extents(bAnimContext *ac,
for (ale = anim_data.first; ale; ale = ale->next) { for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale); AnimData *adt = ANIM_nla_mapping_get(ac, ale);
FCurve *fcu = (FCurve *)ale->key_data; FCurve *fcu = (FCurve *)ale->key_data;
float txmin, txmax, tymin, tymax; rctf bounds;
float unitFac, offset; float unitFac, offset;
/* Get range. */ /* Get range. */
if (BKE_fcurve_calc_bounds( if (BKE_fcurve_calc_bounds(fcu, do_sel_only, include_handles, NULL, &bounds)) {
fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles, NULL)) {
short mapping_flag = ANIM_get_normalization_flags(ac); short mapping_flag = ANIM_get_normalization_flags(ac);
/* Apply NLA scaling. */ /* Apply NLA scaling. */
if (adt) { if (adt) {
txmin = BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP); bounds.xmin = BKE_nla_tweakedit_remap(adt, bounds.xmin, NLATIME_CONVERT_MAP);
txmax = BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP); bounds.xmax = BKE_nla_tweakedit_remap(adt, bounds.xmax, NLATIME_CONVERT_MAP);
} }
/* Apply unit corrections. */ /* Apply unit corrections. */
unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset);
tymin += offset; bounds.ymin += offset;
tymax += offset; bounds.ymax += offset;
tymin *= unitFac; bounds.ymin *= unitFac;
tymax *= unitFac; bounds.ymax *= unitFac;
/* Try to set cur using these values, if they're more extreme than previously set values. /* Try to set cur using these values, if they're more extreme than previously set values.
*/ */
if ((xmin) && (txmin < *xmin)) { if ((xmin) && (bounds.xmin < *xmin)) {
*xmin = txmin; *xmin = bounds.xmin;
} }
if ((xmax) && (txmax > *xmax)) { if ((xmax) && (bounds.xmax > *xmax)) {
*xmax = txmax; *xmax = bounds.xmax;
} }
if ((ymin) && (tymin < *ymin)) { if ((ymin) && (bounds.ymin < *ymin)) {
*ymin = tymin; *ymin = bounds.ymin;
} }
if ((ymax) && (tymax > *ymax)) { if ((ymax) && (bounds.ymax > *ymax)) {
*ymax = tymax; *ymax = bounds.ymax;
} }
foundBounds = true; foundBounds = true;

View File

@ -600,7 +600,7 @@ static void rna_FCurve_group_set(PointerRNA *ptr,
/* calculate time extents of F-Curve */ /* calculate time extents of F-Curve */
static void rna_FCurve_range(FCurve *fcu, float range[2]) static void rna_FCurve_range(FCurve *fcu, float range[2])
{ {
BKE_fcurve_calc_range(fcu, range, range + 1, false, false); BKE_fcurve_calc_range(fcu, range, range + 1, false);
} }
static bool rna_FCurve_is_empty_get(PointerRNA *ptr) static bool rna_FCurve_is_empty_get(PointerRNA *ptr)