Mesh: Move functions to C++ header #105416
@ -1934,7 +1934,7 @@ if(FIRST_RUN)
|
|||||||
info_cfg_option(WITH_IMAGE_OPENEXR)
|
info_cfg_option(WITH_IMAGE_OPENEXR)
|
||||||
info_cfg_option(WITH_IMAGE_OPENJPEG)
|
info_cfg_option(WITH_IMAGE_OPENJPEG)
|
||||||
info_cfg_option(WITH_IMAGE_TIFF)
|
info_cfg_option(WITH_IMAGE_TIFF)
|
||||||
|
|
||||||
info_cfg_text("Audio:")
|
info_cfg_text("Audio:")
|
||||||
info_cfg_option(WITH_CODEC_AVI)
|
info_cfg_option(WITH_CODEC_AVI)
|
||||||
info_cfg_option(WITH_CODEC_FFMPEG)
|
info_cfg_option(WITH_CODEC_FFMPEG)
|
||||||
|
@ -3,23 +3,26 @@
|
|||||||
#include "GHOST_NDOFManagerUnix.h"
|
#include "GHOST_NDOFManagerUnix.h"
|
||||||
#include "GHOST_System.h"
|
#include "GHOST_System.h"
|
||||||
|
|
||||||
|
/* Logging, use `ghost.ndof.unix.*` prefix. */
|
||||||
|
#include "CLG_log.h"
|
||||||
|
|
||||||
#include <spnav.h>
|
#include <spnav.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define SPNAV_SOCK_PATH "/var/run/spnav.sock"
|
static const char *spnav_sock_path = "/var/run/spnav.sock";
|
||||||
|
|
||||||
|
static CLG_LogRef LOG_NDOF_UNIX = {"ghost.ndof.unix"};
|
||||||
|
#define LOG (&LOG_NDOF_UNIX)
|
||||||
|
|
||||||
GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
|
GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
|
||||||
: GHOST_NDOFManager(sys), available_(false)
|
: GHOST_NDOFManager(sys), available_(false)
|
||||||
{
|
{
|
||||||
if (access(SPNAV_SOCK_PATH, F_OK) != 0) {
|
if (access(spnav_sock_path, F_OK) != 0) {
|
||||||
#ifdef DEBUG
|
CLOG_INFO(LOG, 1, "'spacenavd' not found at \"%s\"", spnav_sock_path);
|
||||||
/* annoying for official builds, just adds noise and most people don't own these */
|
|
||||||
puts("ndof: spacenavd not found");
|
|
||||||
/* This isn't a hard error, just means the user doesn't have a 3D mouse. */
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else if (spnav_open() != -1) {
|
else if (spnav_open() != -1) {
|
||||||
|
CLOG_INFO(LOG, 1, "'spacenavd' found at\"%s\"", spnav_sock_path);
|
||||||
available_ = true;
|
available_ = true;
|
||||||
|
|
||||||
/* determine exactly which device (if any) is plugged in */
|
/* determine exactly which device (if any) is plugged in */
|
||||||
|
@ -6318,6 +6318,7 @@ def km_sculpt_expand_modal(_params):
|
|||||||
])
|
])
|
||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
|
|
||||||
def km_sculpt_mesh_filter_modal_map(_params):
|
def km_sculpt_mesh_filter_modal_map(_params):
|
||||||
items = []
|
items = []
|
||||||
keymap = (
|
keymap = (
|
||||||
|
@ -3260,13 +3260,13 @@ class VIEW3D_MT_sculpt(Menu):
|
|||||||
props = layout.operator("sculpt.trim_box_gesture", text="Box Trim")
|
props = layout.operator("sculpt.trim_box_gesture", text="Box Trim")
|
||||||
props.trim_mode = 'DIFFERENCE'
|
props.trim_mode = 'DIFFERENCE'
|
||||||
|
|
||||||
layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
|
props = layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
|
||||||
props.trim_mode = 'DIFFERENCE'
|
props.trim_mode = 'DIFFERENCE'
|
||||||
|
|
||||||
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
|
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
|
||||||
props.trim_mode = 'JOIN'
|
props.trim_mode = 'JOIN'
|
||||||
|
|
||||||
layout.operator("sculpt.trim_lasso_gesture", text="Lasso Add")
|
props = layout.operator("sculpt.trim_lasso_gesture", text="Lasso Add")
|
||||||
props.trim_mode = 'JOIN'
|
props.trim_mode = 'JOIN'
|
||||||
|
|
||||||
layout.operator("sculpt.project_line_gesture", text="Line Project")
|
layout.operator("sculpt.project_line_gesture", text="Line Project")
|
||||||
|
@ -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`.
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -93,7 +93,8 @@ static const Edge<CoordSpace::Tile> convert_coord_space(const Edge<CoordSpace::U
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class NonManifoldTileEdges : public Vector<Edge<CoordSpace::Tile>> {};
|
class NonManifoldTileEdges : public Vector<Edge<CoordSpace::Tile>> {
|
||||||
|
};
|
||||||
|
|
||||||
class NonManifoldUVEdges : public Vector<Edge<CoordSpace::UV>> {
|
class NonManifoldUVEdges : public Vector<Edge<CoordSpace::UV>> {
|
||||||
public:
|
public:
|
||||||
@ -221,7 +222,7 @@ struct Rows {
|
|||||||
/** This pixel is directly affected by a brush and doesn't need to be solved. */
|
/** This pixel is directly affected by a brush and doesn't need to be solved. */
|
||||||
Brush,
|
Brush,
|
||||||
SelectedForCloserExamination,
|
SelectedForCloserExamination,
|
||||||
/** This pixel will be copid from another pixel to solve non-manifold edge bleeding. */
|
/** This pixel will be copied from another pixel to solve non-manifold edge bleeding. */
|
||||||
CopyFromClosestEdge,
|
CopyFromClosestEdge,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,7 +233,7 @@ struct Rows {
|
|||||||
/**
|
/**
|
||||||
* Index of the edge in the list of non-manifold edges.
|
* Index of the edge in the list of non-manifold edges.
|
||||||
*
|
*
|
||||||
* The edge is kept to calculate athe mix factor between the two pixels that have chosen to
|
* The edge is kept to calculate the mix factor between the two pixels that have chosen to
|
||||||
* be mixed.
|
* be mixed.
|
||||||
*/
|
*/
|
||||||
int64_t edge_index;
|
int64_t edge_index;
|
||||||
@ -308,7 +309,7 @@ struct Rows {
|
|||||||
* Look for a second source pixel that will be blended with the first source pixel to improve
|
* Look for a second source pixel that will be blended with the first source pixel to improve
|
||||||
* the quality of the fix.
|
* the quality of the fix.
|
||||||
*
|
*
|
||||||
* - The second source pixel must be a neighbour pixel of the first source, or the same as the
|
* - The second source pixel must be a neighbor pixel of the first source, or the same as the
|
||||||
* first source when no second pixel could be found.
|
* first source when no second pixel could be found.
|
||||||
* - The second source pixel must be a pixel that is painted on by the brush.
|
* - The second source pixel must be a pixel that is painted on by the brush.
|
||||||
* - The second source pixel must be the second closest pixel , or the first source
|
* - The second source pixel must be the second closest pixel , or the first source
|
||||||
|
@ -21,4 +21,4 @@ void BKE_pbvh_pixels_copy_update(PBVH &pbvh,
|
|||||||
ImageUser &image_user,
|
ImageUser &image_user,
|
||||||
const uv_islands::MeshData &mesh_data);
|
const uv_islands::MeshData &mesh_data);
|
||||||
|
|
||||||
} // namespace blender::bke::pbvh::pixels
|
} // namespace blender::bke::pbvh::pixels
|
||||||
|
@ -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 °rees)
|
static AngleRadianBase from_degree(const T °rees)
|
||||||
{
|
{
|
||||||
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 °rees)
|
static AngleCartesianBase from_degree(const T °rees)
|
||||||
{
|
{
|
||||||
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_);
|
||||||
@ -680,69 +676,69 @@ template<typename T = float> struct AngleFraction {
|
|||||||
default:
|
default:
|
||||||
BLI_assert_unreachable();
|
BLI_assert_unreachable();
|
||||||
}
|
}
|
||||||
/* Resulting angle should be oscilating in [0..pi/4] range. */
|
/* Resulting angle should be oscillating in [0..pi/4] range. */
|
||||||
BLI_assert(a.numerator_ >= 0 && a.numerator_ <= a.denominator_ / 4);
|
BLI_assert(a.numerator_ >= 0 && a.numerator_ <= a.denominator_ / 4);
|
||||||
T angle = T(M_PI) * (T(a.numerator_) / T(a.denominator_));
|
T angle = T(M_PI) * (T(a.numerator_) / T(a.denominator_));
|
||||||
x = math::cos(angle);
|
x = math::cos(angle);
|
||||||
y = math::sin(angle);
|
y = math::sin(angle);
|
||||||
/* Diagonal symetry "unfolding". */
|
/* Diagonal symmetry "unfolding". */
|
||||||
if (ELEM(octant, 1, 2)) {
|
if (ELEM(octant, 1, 2)) {
|
||||||
std::swap(x, y);
|
std::swap(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Y axis symetry. */
|
/* Y axis symmetry. */
|
||||||
if (octant >= 2) {
|
if (octant >= 2) {
|
||||||
x = -x;
|
x = -x;
|
||||||
}
|
}
|
||||||
/* X axis symetry. */
|
/* X axis symmetry. */
|
||||||
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
|
||||||
|
|
||||||
|
@ -13,32 +13,29 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
/* TODO: After merge to limit side effects. */
|
BLI_assert(is_unit_scale(axis));
|
||||||
// BLI_assert(is_unit_scale(axis));
|
axis_ = axis;
|
||||||
// axis_ = axis;
|
angle_ = angle;
|
||||||
T length;
|
|
||||||
this->axis_ = math::normalize_and_get_length(axis, length);
|
|
||||||
this->angle_ = 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)
|
||||||
{
|
{
|
||||||
this->axis_ = VecBase<T, 3>(axis);
|
axis_ = to_vector<VecBase<T, 3>>(axis);
|
||||||
this->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));
|
||||||
@ -64,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()));
|
||||||
|
|
||||||
@ -82,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
@ -92,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));
|
||||||
|
@ -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,14 +25,7 @@
|
|||||||
|
|
||||||
namespace blender::math {
|
namespace blender::math {
|
||||||
|
|
||||||
namespace detail {
|
template<typename T, typename AngleT> struct AxisAngleBase {
|
||||||
|
|
||||||
/* Forward declaration for casting operators. */
|
|
||||||
template<typename T> struct Euler3;
|
|
||||||
template<typename T> struct EulerXYZ;
|
|
||||||
template<typename T> struct Quaternion;
|
|
||||||
|
|
||||||
template<typename T, typename AngleT> struct AxisAngle {
|
|
||||||
using vec3_type = VecBase<T, 3>;
|
using vec3_type = VecBase<T, 3>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -41,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 {};
|
||||||
}
|
}
|
||||||
@ -82,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
|
||||||
|
|
||||||
|
@ -90,8 +90,10 @@ template<typename T> inline T floor(const T &a)
|
|||||||
return std::floor(a);
|
return std::floor(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Repeats the sawtooth pattern even on negative numbers.
|
/**
|
||||||
* ex: 'mod_periodic(-3, 4) = 1', 'mod(-3, 4)= -3' */
|
* Repeats the saw-tooth pattern even on negative numbers.
|
||||||
|
* ex: `mod_periodic(-3, 4) = 1`, `mod(-3, 4)= -3`
|
||||||
|
*/
|
||||||
template<typename T> inline T mod_periodic(const T &a, const T &b)
|
template<typename T> inline T mod_periodic(const T &a, const T &b)
|
||||||
{
|
{
|
||||||
return a - (b * math::floor(a / b));
|
return a - (b * math::floor(a / b));
|
||||||
|
@ -455,8 +455,8 @@ struct CartesianBasis {
|
|||||||
const AxisSigned b_forward)
|
const AxisSigned b_forward)
|
||||||
{
|
{
|
||||||
/* Pick predictable next axis. */
|
/* Pick predictable next axis. */
|
||||||
AxisSigned a_up = AxisSigned(abs(a_forward.next_after()));
|
AxisSigned a_up = abs(a_forward.next_after());
|
||||||
AxisSigned b_up = AxisSigned(abs(b_forward.next_after()));
|
AxisSigned b_up = abs(b_forward.next_after());
|
||||||
|
|
||||||
if (sign(a_forward) != sign(b_forward)) {
|
if (sign(a_forward) != sign(b_forward)) {
|
||||||
/* Flip both axis (up and right) so resulting rotation matrix sign remains positive. */
|
/* Flip both axis (up and right) so resulting rotation matrix sign remains positive. */
|
||||||
|
@ -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));
|
||||||
|
@ -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,28 +128,24 @@ template<typename T> struct EulerBase {
|
|||||||
/** \name EulerXYZ
|
/** \name EulerXYZ
|
||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
/* Forward declaration for casting operators. */
|
template<typename T> struct EulerXYZBase : public EulerBase<T> {
|
||||||
template<typename T, typename AngleT> struct AxisAngle;
|
using AngleT = AngleRadianBase<T>;
|
||||||
template<typename T> struct Quaternion;
|
|
||||||
|
|
||||||
template<typename T> struct EulerXYZ : public EulerBase<T> {
|
|
||||||
using AngleT = AngleRadian<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;
|
||||||
@ -159,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. */
|
||||||
@ -169,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.
|
||||||
@ -177,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);
|
||||||
}
|
}
|
||||||
@ -203,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. */
|
||||||
@ -215,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()];
|
||||||
@ -240,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
|
||||||
@ -248,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;
|
||||||
}
|
}
|
||||||
@ -266,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. */
|
||||||
|
|
||||||
@ -323,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_;
|
||||||
}
|
}
|
||||||
@ -436,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
|
||||||
|
|
||||||
|
@ -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, bool Normalized = false>
|
template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat);
|
||||||
[[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, 4, 4> &mat);
|
||||||
template<typename T, bool Normalized = false>
|
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, 3, 3> &mat, EulerOrder order);
|
||||||
template<typename T, bool Normalized = false>
|
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, 4, 4> &mat, EulerOrder order);
|
||||||
template<typename T, bool Normalized = false>
|
|
||||||
[[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.
|
||||||
@ -272,26 +270,26 @@ template<typename T, bool Normalized = false>
|
|||||||
* \return the rotation with the smallest values from the potential candidates.
|
* \return the rotation with the smallest values from the potential candidates.
|
||||||
* \note this correspond to the C API "to_compatible" functions.
|
* \note this correspond to the C API "to_compatible" functions.
|
||||||
*/
|
*/
|
||||||
template<typename T, bool Normalized = false>
|
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, bool Normalized = false>
|
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, bool Normalized = false>
|
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, bool Normalized = false>
|
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, bool Normalized = false>
|
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, bool Normalized = false>
|
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,76 +1054,59 @@ 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, bool Normalized>
|
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);
|
||||||
if constexpr (Normalized) {
|
detail::normalized_to_eul2(mat, eul1, eul2);
|
||||||
detail::normalized_to_eul2(mat, eul1, eul2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
detail::normalized_to_eul2(normalize(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, bool Normalized>
|
template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat)
|
||||||
[[nodiscard]] inline detail::EulerXYZ<T> to_euler(const MatBase<T, 3, 3> &mat)
|
|
||||||
{
|
{
|
||||||
detail::EulerXYZ<T> eul1, eul2;
|
EulerXYZBase<T> eul1, eul2;
|
||||||
if constexpr (Normalized) {
|
detail::normalized_to_eul2(mat, eul1, eul2);
|
||||||
detail::normalized_to_eul2(mat, eul1, eul2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
detail::normalized_to_eul2(normalize(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, bool Normalized>
|
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, Normalized>(MatBase<T, 3, 3>(mat), order);
|
return to_euler<T>(MatBase<T, 3, 3>(mat), order);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 4, 4> &mat)
|
||||||
[[nodiscard]] inline detail::EulerXYZ<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, Normalized>(MatBase<T, 3, 3>(mat));
|
return to_euler<T>(MatBase<T, 3, 3>(mat));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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());
|
||||||
if constexpr (Normalized) {
|
detail::normalized_to_eul2(mat, eul1, eul2);
|
||||||
detail::normalized_to_eul2(mat, eul1, eul2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
detail::normalized_to_eul2(normalize(mat), eul1, eul2);
|
|
||||||
}
|
|
||||||
eul1 = eul1.wrapped_around(reference);
|
eul1 = eul1.wrapped_around(reference);
|
||||||
eul2 = eul2.wrapped_around(reference);
|
eul2 = eul2.wrapped_around(reference);
|
||||||
/* Return best, which is just the one with lowest values it in. */
|
/* Return best, which is just the one with lowest values it in. */
|
||||||
@ -1140,17 +1116,12 @@ template<typename T, bool Normalized>
|
|||||||
eul1;
|
eul1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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;
|
||||||
if constexpr (Normalized) {
|
detail::normalized_to_eul2(mat, eul1, eul2);
|
||||||
detail::normalized_to_eul2(mat, eul1, eul2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
detail::normalized_to_eul2(normalize(mat), eul1, eul2);
|
|
||||||
}
|
|
||||||
eul1 = eul1.wrapped_around(reference);
|
eul1 = eul1.wrapped_around(reference);
|
||||||
eul2 = eul2.wrapped_around(reference);
|
eul2 = eul2.wrapped_around(reference);
|
||||||
/* Return best, which is just the one with lowest values it in. */
|
/* Return best, which is just the one with lowest values it in. */
|
||||||
@ -1160,39 +1131,33 @@ template<typename T, bool Normalized>
|
|||||||
eul1;
|
eul1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(MatBase<T, 3, 3>(mat), reference);
|
return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(MatBase<T, 3, 3>(mat), reference);
|
return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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)
|
||||||
{
|
{
|
||||||
using namespace math;
|
return detail::normalized_to_quat_with_checks(mat);
|
||||||
if constexpr (Normalized) {
|
|
||||||
return detail::normalized_to_quat_with_checks(mat);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return detail::normalized_to_quat_with_checks(normalize(mat));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(MatBase<T, 3, 3>(mat));
|
return to_quaternion<T>(MatBase<T, 3, 3>(mat));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool AllowNegativeScale, typename T, int NumCol, int NumRow>
|
template<bool AllowNegativeScale, typename T, int NumCol, int NumRow>
|
||||||
@ -1222,22 +1187,22 @@ template<bool AllowNegativeScale, typename T>
|
|||||||
/* Implementation details. Use `to_euler` and `to_quaternion` instead. */
|
/* Implementation details. Use `to_euler` and `to_quaternion` instead. */
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(mat);
|
r_rotation = to_quaternion<T>(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(mat);
|
r_rotation = to_euler<T>(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Normalized>
|
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, Normalized>(mat, r_rotation.order());
|
r_rotation = to_euler<T>(mat, r_rotation.order());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@ -1254,7 +1219,7 @@ inline void to_rot_scale(const MatBase<T, 3, 3> &mat,
|
|||||||
r_scale = -r_scale;
|
r_scale = -r_scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
detail::to_rotation<T, true>(normalized_mat, r_rotation);
|
detail::to_rotation<T>(normalized_mat, r_rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool AllowNegativeScale, typename T, typename RotationT>
|
template<bool AllowNegativeScale, typename T, typename RotationT>
|
||||||
@ -1379,8 +1344,8 @@ template<typename MatT> [[nodiscard]] MatT orthogonalize(const MatT &mat, const
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The secondary axis is chosen as follow (X->Y, Y->X, Z->X).
|
* The secondary axis is chosen as follow (X->Y, Y->X, Z->X).
|
||||||
* If this axis is coplanar try the third axis.
|
* If this axis is co-planar try the third axis.
|
||||||
* If also coplanar, make up an axis by shuffling the primary axis coordinates (xyz > yzx).
|
* If also co-planar, make up an axis by shuffling the primary axis coordinates (XYZ > YZX).
|
||||||
*/
|
*/
|
||||||
switch (axis) {
|
switch (axis) {
|
||||||
case Axis::X:
|
case Axis::X:
|
||||||
|
@ -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(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(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,21 +645,20 @@ 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));
|
||||||
Mat3T unit_mat = from_rotation<Mat3T>(quat);
|
Mat3T unit_mat = from_rotation<Mat3T>(quat);
|
||||||
return to_euler<T, true>(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));
|
||||||
Mat3T unit_mat = from_rotation<Mat3T>(quat);
|
Mat3T unit_mat = from_rotation<Mat3T>(quat);
|
||||||
return to_euler<T, true>(unit_mat, order);
|
return to_euler<T>(unit_mat, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
@ -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();
|
||||||
|
@ -18,51 +18,46 @@ namespace blender::math {
|
|||||||
/** \name Quaternion
|
/** \name Quaternion
|
||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
/* Forward declaration for casting operators. */
|
|
||||||
template<typename T> struct EulerXYZ;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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};
|
||||||
}
|
}
|
||||||
@ -71,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. */
|
||||||
|
|
||||||
@ -90,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).
|
||||||
@ -128,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,
|
||||||
@ -140,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
|
||||||
@ -201,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.
|
||||||
@ -209,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. */
|
||||||
@ -235,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";
|
||||||
@ -284,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -313,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
|
||||||
|
|
||||||
|
@ -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)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,10 +265,10 @@ set(SRC
|
|||||||
BLI_math_angle_types.hh
|
BLI_math_angle_types.hh
|
||||||
BLI_math_axis_angle.hh
|
BLI_math_axis_angle.hh
|
||||||
BLI_math_axis_angle_types.hh
|
BLI_math_axis_angle_types.hh
|
||||||
BLI_math_basis_types.hh
|
|
||||||
BLI_math_base.h
|
BLI_math_base.h
|
||||||
BLI_math_base.hh
|
BLI_math_base.hh
|
||||||
BLI_math_base_safe.h
|
BLI_math_base_safe.h
|
||||||
|
BLI_math_basis_types.hh
|
||||||
BLI_math_bits.h
|
BLI_math_bits.h
|
||||||
BLI_math_boolean.hh
|
BLI_math_boolean.hh
|
||||||
BLI_math_color.h
|
BLI_math_color.h
|
||||||
|
@ -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(U_A);
|
QuaternionBase<T> quat_A = math::to_quaternion(normalize(U_A));
|
||||||
detail::Quaternion<T> quat_B = math::to_quaternion(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
|
||||||
|
|
||||||
|
@ -347,8 +347,6 @@ TEST(math_matrix, MatrixMethods)
|
|||||||
float3 expect_scale = float3(3, 2, 2);
|
float3 expect_scale = float3(3, 2, 2);
|
||||||
float3 expect_location = float3(0, 1, 0);
|
float3 expect_location = float3(0, 1, 0);
|
||||||
|
|
||||||
EXPECT_V3_NEAR(float3(to_euler(m)), float3(expect_eul), 0.0002f);
|
|
||||||
EXPECT_V4_NEAR(float4(to_quaternion(m)), float4(expect_qt), 0.0002f);
|
|
||||||
EXPECT_EQ(to_scale(m), expect_scale);
|
EXPECT_EQ(to_scale(m), expect_scale);
|
||||||
|
|
||||||
float4 expect_sz = {3, 2, 2, M_SQRT2};
|
float4 expect_sz = {3, 2, 2, M_SQRT2};
|
||||||
@ -360,6 +358,9 @@ TEST(math_matrix, MatrixMethods)
|
|||||||
float4x4 m2 = normalize(m);
|
float4x4 m2 = normalize(m);
|
||||||
EXPECT_TRUE(is_unit_scale(m2));
|
EXPECT_TRUE(is_unit_scale(m2));
|
||||||
|
|
||||||
|
EXPECT_V3_NEAR(float3(to_euler(m1)), float3(expect_eul), 0.0002f);
|
||||||
|
EXPECT_V4_NEAR(float4(to_quaternion(m1)), float4(expect_qt), 0.0002f);
|
||||||
|
|
||||||
EulerXYZ eul;
|
EulerXYZ eul;
|
||||||
Quaternion qt;
|
Quaternion qt;
|
||||||
float3 scale;
|
float3 scale;
|
||||||
|
@ -304,7 +304,7 @@ TEST(math_rotation, RotateDirectionAroundAxis)
|
|||||||
|
|
||||||
TEST(math_rotation, AxisAngleConstructors)
|
TEST(math_rotation, AxisAngleConstructors)
|
||||||
{
|
{
|
||||||
AxisAngle a({0.0f, 0.0f, 2.0f}, M_PI_2);
|
AxisAngle a({0.0f, 0.0f, 1.0f}, M_PI_2);
|
||||||
EXPECT_V3_NEAR(a.axis(), float3(0, 0, 1), 1e-4);
|
EXPECT_V3_NEAR(a.axis(), float3(0, 0, 1), 1e-4);
|
||||||
EXPECT_NEAR(float(a.angle()), M_PI_2, 1e-4);
|
EXPECT_NEAR(float(a.angle()), M_PI_2, 1e-4);
|
||||||
EXPECT_NEAR(sin(a.angle()), 1.0f, 1e-4);
|
EXPECT_NEAR(sin(a.angle()), 1.0f, 1e-4);
|
||||||
@ -316,6 +316,10 @@ TEST(math_rotation, AxisAngleConstructors)
|
|||||||
EXPECT_NEAR(b.angle().sin(), 1.0f, 1e-4);
|
EXPECT_NEAR(b.angle().sin(), 1.0f, 1e-4);
|
||||||
EXPECT_NEAR(b.angle().cos(), 0.0f, 1e-4);
|
EXPECT_NEAR(b.angle().cos(), 0.0f, 1e-4);
|
||||||
|
|
||||||
|
AxisAngle axis_angle_basis = AxisAngle(AxisSigned::Y_NEG, M_PI);
|
||||||
|
EXPECT_EQ(axis_angle_basis.axis(), float3(0.0f, -1.0f, 0.0f));
|
||||||
|
EXPECT_EQ(axis_angle_basis.angle(), M_PI);
|
||||||
|
|
||||||
AxisAngle c({1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f});
|
AxisAngle c({1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f});
|
||||||
EXPECT_V3_NEAR(c.axis(), float3(0, 0, 1), 1e-4);
|
EXPECT_V3_NEAR(c.axis(), float3(0, 0, 1), 1e-4);
|
||||||
EXPECT_NEAR(float(c.angle()), M_PI_2, 1e-4);
|
EXPECT_NEAR(float(c.angle()), M_PI_2, 1e-4);
|
||||||
|
@ -271,7 +271,7 @@ TEST(math_rotation_types, AngleFraction)
|
|||||||
EXPECT_NEAR(angle.cos(), cos((T(M_PI) * i) / 16), 1e-6f);
|
EXPECT_NEAR(angle.cos(), cos((T(M_PI) * i) / 16), 1e-6f);
|
||||||
EXPECT_NEAR(angle.sin(), sin((T(M_PI) * i) / 16), 1e-6f);
|
EXPECT_NEAR(angle.sin(), sin((T(M_PI) * i) / 16), 1e-6f);
|
||||||
|
|
||||||
/* Ensure symetry. */
|
/* Ensure symmetry. */
|
||||||
AngleCartesian angle_opposite(pi + pi * i / 16);
|
AngleCartesian angle_opposite(pi + pi * i / 16);
|
||||||
EXPECT_EQ(angle.cos(), -angle_opposite.cos());
|
EXPECT_EQ(angle.cos(), -angle_opposite.cos());
|
||||||
EXPECT_EQ(angle.sin(), -angle_opposite.sin());
|
EXPECT_EQ(angle.sin(), -angle_opposite.sin());
|
||||||
@ -308,6 +308,7 @@ TEST(math_rotation_types, AngleFraction)
|
|||||||
EXPECT_EQ(AngleCartesian(-pi * 1 / 4).sin(), T(-M_SQRT1_2));
|
EXPECT_EQ(AngleCartesian(-pi * 1 / 4).sin(), T(-M_SQRT1_2));
|
||||||
EXPECT_EQ(AngleCartesian(-pi * 3 / 4).sin(), T(-M_SQRT1_2));
|
EXPECT_EQ(AngleCartesian(-pi * 3 / 4).sin(), T(-M_SQRT1_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(math_rotation_types, TypeConversion)
|
TEST(math_rotation_types, TypeConversion)
|
||||||
{
|
{
|
||||||
/* All the same rotation. */
|
/* All the same rotation. */
|
||||||
@ -315,7 +316,8 @@ TEST(math_rotation_types, TypeConversion)
|
|||||||
EulerXYZ euler_xyz(AngleRadian::from_degree(20.0559),
|
EulerXYZ euler_xyz(AngleRadian::from_degree(20.0559),
|
||||||
AngleRadian::from_degree(-20.5632f),
|
AngleRadian::from_degree(-20.5632f),
|
||||||
AngleRadian::from_degree(30.3091f));
|
AngleRadian::from_degree(30.3091f));
|
||||||
AxisAngle axis_angle({0.563771, -0.333098, 0.755783}, AngleRadian::from_degree(44.0284f));
|
AxisAngle axis_angle(normalize(float3{0.563771, -0.333098, 0.755783}),
|
||||||
|
AngleRadian::from_degree(44.0284f));
|
||||||
|
|
||||||
EXPECT_V4_NEAR(float4(to_quaternion(euler_xyz)), float4(quaternion), 1e-4);
|
EXPECT_V4_NEAR(float4(to_quaternion(euler_xyz)), float4(quaternion), 1e-4);
|
||||||
EXPECT_V3_NEAR(to_axis_angle(euler_xyz).axis(), axis_angle.axis(), 1e-4);
|
EXPECT_V3_NEAR(to_axis_angle(euler_xyz).axis(), axis_angle.axis(), 1e-4);
|
||||||
@ -394,12 +396,12 @@ TEST(math_rotation_types, Euler3Conversion)
|
|||||||
EXPECT_V3_NEAR(float3(to_euler(mat_zxy, EulerOrder::ZXY).xyz()), xyz, 1e-4);
|
EXPECT_V3_NEAR(float3(to_euler(mat_zxy, EulerOrder::ZXY).xyz()), xyz, 1e-4);
|
||||||
EXPECT_V3_NEAR(float3(to_euler(mat_zyx, EulerOrder::ZYX).xyz()), xyz, 1e-4);
|
EXPECT_V3_NEAR(float3(to_euler(mat_zyx, EulerOrder::ZYX).xyz()), xyz, 1e-4);
|
||||||
|
|
||||||
AxisAngle axis_angle_xyz = AxisAngle({0.563771, -0.333098, 0.755783}, 0.76844f);
|
AxisAngle axis_angle_xyz = {normalize(float3{0.563771, -0.333098, 0.755783}), 0.76844f};
|
||||||
AxisAngle axis_angle_xzy = AxisAngle({0.359907, -0.376274, 0.853747}, 0.676476f);
|
AxisAngle axis_angle_xzy = {normalize(float3{0.359907, -0.376274, 0.853747}), 0.676476f};
|
||||||
AxisAngle axis_angle_yxz = AxisAngle({0.636846, -0.376274, 0.672937}, 0.676476f);
|
AxisAngle axis_angle_yxz = {normalize(float3{0.636846, -0.376274, 0.672937}), 0.676476f};
|
||||||
AxisAngle axis_angle_yzx = AxisAngle({0.563771, -0.572084, 0.59572}, 0.76844f);
|
AxisAngle axis_angle_yzx = {normalize(float3{0.563771, -0.572084, 0.59572}), 0.76844f};
|
||||||
AxisAngle axis_angle_zxy = AxisAngle({0.318609, -0.572084, 0.755783}, 0.76844f);
|
AxisAngle axis_angle_zxy = {normalize(float3{0.318609, -0.572084, 0.755783}), 0.76844f};
|
||||||
AxisAngle axis_angle_zyx = AxisAngle({0.359907, -0.646237, 0.672937}, 0.676476f);
|
AxisAngle axis_angle_zyx = {normalize(float3{0.359907, -0.646237, 0.672937}), 0.676476f};
|
||||||
|
|
||||||
EXPECT_V3_NEAR(to_axis_angle(euler3_xyz).axis(), axis_angle_xyz.axis(), 1e-4);
|
EXPECT_V3_NEAR(to_axis_angle(euler3_xyz).axis(), axis_angle_xyz.axis(), 1e-4);
|
||||||
EXPECT_V3_NEAR(to_axis_angle(euler3_xzy).axis(), axis_angle_xzy.axis(), 1e-4);
|
EXPECT_V3_NEAR(to_axis_angle(euler3_xzy).axis(), axis_angle_xzy.axis(), 1e-4);
|
||||||
|
@ -10,4 +10,4 @@ void main()
|
|||||||
tilemaps_clip_buf[index].clip_near = floatBitsToOrderedInt(-FLT_MAX);
|
tilemaps_clip_buf[index].clip_near = floatBitsToOrderedInt(-FLT_MAX);
|
||||||
tilemaps_clip_buf[index].clip_far = floatBitsToOrderedInt(FLT_MAX);
|
tilemaps_clip_buf[index].clip_far = floatBitsToOrderedInt(FLT_MAX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -264,7 +264,7 @@ static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
|
|||||||
ac->ads = sipo->ads;
|
ac->ads = sipo->ads;
|
||||||
|
|
||||||
/* set settings for Graph Editor - "Selected = Editable" */
|
/* set settings for Graph Editor - "Selected = Editable" */
|
||||||
if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
|
if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
|
||||||
sipo->ads->filterflag |= ADS_FILTER_SELEDIT;
|
sipo->ads->filterflag |= ADS_FILTER_SELEDIT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1711,7 +1711,11 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE
|
|||||||
|
|
||||||
static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
|
static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
|
||||||
{
|
{
|
||||||
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||||
Object *object = CTX_data_active_object(C);
|
Object *object = CTX_data_active_object(C);
|
||||||
|
|
||||||
|
BKE_sculpt_update_object_for_edit(depsgraph, object, false, true, false);
|
||||||
|
|
||||||
SculptSession *ss = object->sculpt;
|
SculptSession *ss = object->sculpt;
|
||||||
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
|
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
|
||||||
/* Not supported in Multires and Dyntopo. */
|
/* Not supported in Multires and Dyntopo. */
|
||||||
|
@ -16,9 +16,11 @@
|
|||||||
#include "BLI_hash.h"
|
#include "BLI_hash.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
#include "BLI_math_vector.hh"
|
#include "BLI_math_vector.hh"
|
||||||
|
#include "BLI_math_vector_types.hh"
|
||||||
#include "BLI_span.hh"
|
#include "BLI_span.hh"
|
||||||
#include "BLI_task.h"
|
#include "BLI_task.h"
|
||||||
#include "BLI_task.hh"
|
#include "BLI_task.hh"
|
||||||
|
#include "BLI_vector.hh"
|
||||||
|
|
||||||
#include "DNA_brush_types.h"
|
#include "DNA_brush_types.h"
|
||||||
#include "DNA_customdata_types.h"
|
#include "DNA_customdata_types.h"
|
||||||
@ -53,6 +55,9 @@
|
|||||||
|
|
||||||
#include "bmesh.h"
|
#include "bmesh.h"
|
||||||
|
|
||||||
|
using blender::float3;
|
||||||
|
using blender::Vector;
|
||||||
|
|
||||||
/* Utils. */
|
/* Utils. */
|
||||||
|
|
||||||
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
|
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
|
||||||
@ -1240,34 +1245,45 @@ static void sculpt_face_set_delete_geometry(Object *ob,
|
|||||||
|
|
||||||
static void sculpt_face_set_edit_fair_face_set(Object *ob,
|
static void sculpt_face_set_edit_fair_face_set(Object *ob,
|
||||||
const int active_face_set_id,
|
const int active_face_set_id,
|
||||||
const eMeshFairingDepth fair_order)
|
const eMeshFairingDepth fair_order,
|
||||||
|
const float strength)
|
||||||
{
|
{
|
||||||
SculptSession *ss = ob->sculpt;
|
SculptSession *ss = ob->sculpt;
|
||||||
const int totvert = SCULPT_vertex_count_get(ss);
|
const int totvert = SCULPT_vertex_count_get(ss);
|
||||||
|
|
||||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||||
bool *fair_verts = static_cast<bool *>(
|
Vector<float3> orig_positions;
|
||||||
MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"));
|
Vector<bool> fair_verts;
|
||||||
|
|
||||||
|
orig_positions.resize(totvert);
|
||||||
|
fair_verts.resize(totvert);
|
||||||
|
|
||||||
SCULPT_boundary_info_ensure(ob);
|
SCULPT_boundary_info_ensure(ob);
|
||||||
|
|
||||||
for (int i = 0; i < totvert; i++) {
|
for (int i = 0; i < totvert; i++) {
|
||||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||||
|
|
||||||
|
orig_positions[i] = SCULPT_vertex_co_get(ss, vertex);
|
||||||
fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) &&
|
fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) &&
|
||||||
SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) &&
|
SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) &&
|
||||||
SCULPT_vertex_has_unique_face_set(ss, vertex);
|
SCULPT_vertex_has_unique_face_set(ss, vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
float(*positions)[3] = SCULPT_mesh_deformed_positions_get(ss);
|
float(*positions)[3] = SCULPT_mesh_deformed_positions_get(ss);
|
||||||
BKE_mesh_prefair_and_fair_verts(mesh, positions, fair_verts, fair_order);
|
BKE_mesh_prefair_and_fair_verts(mesh, positions, fair_verts.data(), fair_order);
|
||||||
MEM_freeN(fair_verts);
|
|
||||||
|
for (int i = 0; i < totvert; i++) {
|
||||||
|
if (fair_verts[i]) {
|
||||||
|
interp_v3_v3v3(positions[i], orig_positions[i], positions[i], strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sculpt_face_set_apply_edit(Object *ob,
|
static void sculpt_face_set_apply_edit(Object *ob,
|
||||||
const int active_face_set_id,
|
const int active_face_set_id,
|
||||||
const int mode,
|
const int mode,
|
||||||
const bool modify_hidden)
|
const bool modify_hidden,
|
||||||
|
const float strength = 0.0f)
|
||||||
{
|
{
|
||||||
SculptSession *ss = ob->sculpt;
|
SculptSession *ss = ob->sculpt;
|
||||||
|
|
||||||
@ -1288,10 +1304,12 @@ static void sculpt_face_set_apply_edit(Object *ob,
|
|||||||
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
|
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
|
||||||
break;
|
break;
|
||||||
case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
|
case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
|
||||||
sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION);
|
sculpt_face_set_edit_fair_face_set(
|
||||||
|
ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION, strength);
|
||||||
break;
|
break;
|
||||||
case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
|
case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
|
||||||
sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY);
|
sculpt_face_set_edit_fair_face_set(
|
||||||
|
ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY, strength);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1403,13 +1421,17 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C,
|
|||||||
PBVH *pbvh = ss->pbvh;
|
PBVH *pbvh = ss->pbvh;
|
||||||
PBVHNode **nodes;
|
PBVHNode **nodes;
|
||||||
int totnode;
|
int totnode;
|
||||||
|
|
||||||
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
|
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
|
||||||
|
|
||||||
|
const float strength = RNA_float_get(op->ptr, "strength");
|
||||||
|
|
||||||
SCULPT_undo_push_begin(ob, op);
|
SCULPT_undo_push_begin(ob, op);
|
||||||
for (int i = 0; i < totnode; i++) {
|
for (int i = 0; i < totnode; i++) {
|
||||||
BKE_pbvh_node_mark_update(nodes[i]);
|
BKE_pbvh_node_mark_update(nodes[i]);
|
||||||
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
|
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
|
||||||
}
|
}
|
||||||
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false);
|
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false, strength);
|
||||||
|
|
||||||
if (ss->deform_modifiers_active || ss->shapekey_active) {
|
if (ss->deform_modifiers_active || ss->shapekey_active) {
|
||||||
SCULPT_flush_stroke_deform(sd, ob, true);
|
SCULPT_flush_stroke_deform(sd, ob, true);
|
||||||
@ -1420,32 +1442,37 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C,
|
|||||||
MEM_freeN(nodes);
|
MEM_freeN(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
static bool sculpt_face_set_edit_init(bContext *C, wmOperator *op)
|
||||||
{
|
{
|
||||||
Object *ob = CTX_data_active_object(C);
|
Object *ob = CTX_data_active_object(C);
|
||||||
SculptSession *ss = ob->sculpt;
|
SculptSession *ss = ob->sculpt;
|
||||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||||
|
|
||||||
const eSculptFaceSetEditMode mode = static_cast<eSculptFaceSetEditMode>(
|
const eSculptFaceSetEditMode mode = static_cast<eSculptFaceSetEditMode>(
|
||||||
RNA_enum_get(op->ptr, "mode"));
|
RNA_enum_get(op->ptr, "mode"));
|
||||||
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
|
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
|
||||||
|
|
||||||
if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) {
|
if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) {
|
||||||
return OPERATOR_CANCELLED;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob));
|
ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob));
|
||||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
||||||
|
|
||||||
/* Update the current active Face Set and Vertex as the operator can be used directly from the
|
return true;
|
||||||
* tool without brush cursor. */
|
}
|
||||||
SculptCursorGeometryInfo sgi;
|
|
||||||
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
|
static int sculpt_face_set_edit_exec(bContext *C, wmOperator *op)
|
||||||
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
|
{
|
||||||
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
|
if (!sculpt_face_set_edit_init(C, op)) {
|
||||||
return OPERATOR_CANCELLED;
|
return OPERATOR_CANCELLED;
|
||||||
}
|
}
|
||||||
const int active_face_set = SCULPT_active_face_set_get(ss);
|
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
|
||||||
|
const int active_face_set = RNA_int_get(op->ptr, "active_face_set");
|
||||||
|
const eSculptFaceSetEditMode mode = static_cast<eSculptFaceSetEditMode>(
|
||||||
|
RNA_enum_get(op->ptr, "mode"));
|
||||||
|
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
|
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
|
||||||
@ -1466,6 +1493,27 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
|
|||||||
return OPERATOR_FINISHED;
|
return OPERATOR_FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||||
|
{
|
||||||
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
|
||||||
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
||||||
|
|
||||||
|
/* Update the current active Face Set and Vertex as the operator can be used directly from the
|
||||||
|
* tool without brush cursor. */
|
||||||
|
SculptCursorGeometryInfo sgi;
|
||||||
|
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
|
||||||
|
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
|
||||||
|
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
RNA_int_set(op->ptr, "active_face_set", SCULPT_active_face_set_get(ss));
|
||||||
|
|
||||||
|
return sculpt_face_set_edit_exec(C, op);
|
||||||
|
}
|
||||||
|
|
||||||
void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot)
|
void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot)
|
||||||
{
|
{
|
||||||
/* Identifiers. */
|
/* Identifiers. */
|
||||||
@ -1475,12 +1523,19 @@ void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot)
|
|||||||
|
|
||||||
/* Api callbacks. */
|
/* Api callbacks. */
|
||||||
ot->invoke = sculpt_face_set_edit_invoke;
|
ot->invoke = sculpt_face_set_edit_invoke;
|
||||||
|
ot->exec = sculpt_face_set_edit_exec;
|
||||||
ot->poll = SCULPT_mode_poll;
|
ot->poll = SCULPT_mode_poll;
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
|
||||||
|
|
||||||
|
PropertyRNA *prop = RNA_def_int(
|
||||||
|
ot->srna, "active_face_set", 1, 0, INT_MAX, "Active Face Set", "", 0, 64);
|
||||||
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||||
|
|
||||||
RNA_def_enum(
|
RNA_def_enum(
|
||||||
ot->srna, "mode", prop_sculpt_face_sets_edit_types, SCULPT_FACE_SET_EDIT_GROW, "Mode", "");
|
ot->srna, "mode", prop_sculpt_face_sets_edit_types, SCULPT_FACE_SET_EDIT_GROW, "Mode", "");
|
||||||
|
RNA_def_float(ot->srna, "strength", 1.0f, 0.0f, 1.0f, "Strength", "", 0.0f, 1.0f);
|
||||||
|
|
||||||
ot->prop = RNA_def_boolean(ot->srna,
|
ot->prop = RNA_def_boolean(ot->srna,
|
||||||
"modify_hidden",
|
"modify_hidden",
|
||||||
true,
|
true,
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -268,7 +268,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
|||||||
if (STREQ(column_id.name, "Rotation")) {
|
if (STREQ(column_id.name, "Rotation")) {
|
||||||
return std::make_unique<ColumnValues>(
|
return std::make_unique<ColumnValues>(
|
||||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||||
return float3(math::to_euler(transforms[index]));
|
return float3(math::to_euler(math::normalize(transforms[index])));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (STREQ(column_id.name, "Scale")) {
|
if (STREQ(column_id.name, "Scale")) {
|
||||||
|
@ -195,11 +195,6 @@ struct UnwrapOptions {
|
|||||||
bool pin_unselected;
|
bool pin_unselected;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UnwrapResultInfo {
|
|
||||||
int count_changed;
|
|
||||||
int count_failed;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
|
static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
|
||||||
{
|
{
|
||||||
BMFace *efa;
|
BMFace *efa;
|
||||||
@ -1387,9 +1382,10 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||||||
FaceIsland *face_island = island_vector[i];
|
FaceIsland *face_island = island_vector[i];
|
||||||
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
|
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
|
||||||
pack_island->bounds_rect = face_island->bounds_rect;
|
pack_island->bounds_rect = face_island->bounds_rect;
|
||||||
|
pack_island->caller_index = i;
|
||||||
pack_island_vector.append(pack_island);
|
pack_island_vector.append(pack_island);
|
||||||
}
|
}
|
||||||
BoxPack *box_array = pack_islands(pack_island_vector, *params, scale);
|
pack_islands(pack_island_vector, *params, scale);
|
||||||
|
|
||||||
float base_offset[2] = {0.0f, 0.0f};
|
float base_offset[2] = {0.0f, 0.0f};
|
||||||
copy_v2_v2(base_offset, params->udim_base_offset);
|
copy_v2_v2(base_offset, params->udim_base_offset);
|
||||||
@ -1428,8 +1424,9 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||||||
float matrix[2][2];
|
float matrix[2][2];
|
||||||
float matrix_inverse[2][2];
|
float matrix_inverse[2][2];
|
||||||
float pre_translate[2];
|
float pre_translate[2];
|
||||||
for (int i = 0; i < island_vector.size(); i++) {
|
for (int64_t i : pack_island_vector.index_range()) {
|
||||||
FaceIsland *island = island_vector[box_array[i].index];
|
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
||||||
|
FaceIsland *island = island_vector[pack_island->caller_index];
|
||||||
matrix[0][0] = scale[0];
|
matrix[0][0] = scale[0];
|
||||||
matrix[0][1] = 0.0f;
|
matrix[0][1] = 0.0f;
|
||||||
matrix[1][0] = 0.0f;
|
matrix[1][0] = 0.0f;
|
||||||
@ -1439,11 +1436,16 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||||||
/* Add base_offset, post transform. */
|
/* Add base_offset, post transform. */
|
||||||
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
|
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
|
||||||
|
|
||||||
/* Translate to box_array from bounds_rect. */
|
/* Add pre-translation from #pack_islands. */
|
||||||
blender::geometry::PackIsland *pack_island = pack_island_vector[box_array[i].index];
|
pre_translate[0] += pack_island->pre_translate.x;
|
||||||
pre_translate[0] += box_array[i].x - pack_island->bounds_rect.xmin;
|
pre_translate[1] += pack_island->pre_translate.y;
|
||||||
pre_translate[1] += box_array[i].y - pack_island->bounds_rect.ymin;
|
|
||||||
|
/* Perform the transformation. */
|
||||||
island_uv_transform(island, matrix, pre_translate);
|
island_uv_transform(island, matrix, pre_translate);
|
||||||
|
|
||||||
|
/* Cleanup memory. */
|
||||||
|
pack_island_vector[i] = nullptr;
|
||||||
|
delete pack_island;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||||
@ -1456,14 +1458,6 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||||||
MEM_freeN(island->faces);
|
MEM_freeN(island->faces);
|
||||||
MEM_freeN(island);
|
MEM_freeN(island);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < pack_island_vector.size(); i++) {
|
|
||||||
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
|
||||||
pack_island_vector[i] = nullptr;
|
|
||||||
delete pack_island;
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_freeN(box_array);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
@ -2438,15 +2432,9 @@ static int unwrap_exec(bContext *C, wmOperator *op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* execute unwrap */
|
/* execute unwrap */
|
||||||
UnwrapResultInfo result_info{};
|
int count_changed = 0;
|
||||||
result_info.count_changed = 0;
|
int count_failed = 0;
|
||||||
result_info.count_failed = 0;
|
uvedit_unwrap_multi(scene, objects, objects_len, &options, &count_changed, &count_failed);
|
||||||
uvedit_unwrap_multi(scene,
|
|
||||||
objects,
|
|
||||||
objects_len,
|
|
||||||
&options,
|
|
||||||
&result_info.count_changed,
|
|
||||||
&result_info.count_failed);
|
|
||||||
|
|
||||||
UVPackIsland_Params pack_island_params{};
|
UVPackIsland_Params pack_island_params{};
|
||||||
pack_island_params.rotate = true;
|
pack_island_params.rotate = true;
|
||||||
@ -2464,17 +2452,17 @@ static int unwrap_exec(bContext *C, wmOperator *op)
|
|||||||
|
|
||||||
MEM_freeN(objects);
|
MEM_freeN(objects);
|
||||||
|
|
||||||
if (result_info.count_failed == 0 && result_info.count_changed == 0) {
|
if (count_failed == 0 && count_changed == 0) {
|
||||||
BKE_report(op->reports,
|
BKE_report(op->reports,
|
||||||
RPT_WARNING,
|
RPT_WARNING,
|
||||||
"Unwrap could not solve any island(s), edge seams may need to be added");
|
"Unwrap could not solve any island(s), edge seams may need to be added");
|
||||||
}
|
}
|
||||||
else if (result_info.count_failed) {
|
else if (count_failed) {
|
||||||
BKE_reportf(op->reports,
|
BKE_reportf(op->reports,
|
||||||
RPT_WARNING,
|
RPT_WARNING,
|
||||||
"Unwrap failed to solve %d of %d island(s), edge seams may need to be added",
|
"Unwrap failed to solve %d of %d island(s), edge seams may need to be added",
|
||||||
result_info.count_failed,
|
count_failed,
|
||||||
result_info.count_changed + result_info.count_failed);
|
count_changed + count_failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
return OPERATOR_FINISHED;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
#include "BLI_boxpack_2d.h"
|
#include "BLI_math_matrix.hh"
|
||||||
#include "BLI_span.hh"
|
#include "BLI_span.hh"
|
||||||
|
|
||||||
#include "DNA_vec_types.h"
|
#include "DNA_vec_types.h"
|
||||||
@ -46,10 +46,12 @@ namespace blender::geometry {
|
|||||||
class PackIsland {
|
class PackIsland {
|
||||||
public:
|
public:
|
||||||
rctf bounds_rect;
|
rctf bounds_rect;
|
||||||
|
float2 pre_translate; /* Output. */
|
||||||
|
int caller_index; /* Unchanged by #pack_islands, used by caller. */
|
||||||
};
|
};
|
||||||
|
|
||||||
BoxPack *pack_islands(const Span<PackIsland *> &island_vector,
|
void pack_islands(const Span<PackIsland *> &islands,
|
||||||
const UVPackIsland_Params ¶ms,
|
const UVPackIsland_Params ¶ms,
|
||||||
float r_scale[2]);
|
float r_scale[2]);
|
||||||
|
|
||||||
} // namespace blender::geometry
|
} // namespace blender::geometry
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "BLI_convexhull_2d.h"
|
#include "BLI_convexhull_2d.h"
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_listbase.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
#include "BLI_math_matrix.hh"
|
|
||||||
#include "BLI_rect.h"
|
#include "BLI_rect.h"
|
||||||
#include "BLI_vector.hh"
|
#include "BLI_vector.hh"
|
||||||
|
|
||||||
@ -302,9 +301,9 @@ static float calc_margin_from_aabb_length_sum(const Span<PackIsland *> &island_v
|
|||||||
return params.margin * aabb_length_sum * 0.1f;
|
return params.margin * aabb_length_sum * 0.1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxPack *pack_islands(const Span<PackIsland *> &island_vector,
|
static BoxPack *pack_islands_box_array(const Span<PackIsland *> &island_vector,
|
||||||
const UVPackIsland_Params ¶ms,
|
const UVPackIsland_Params ¶ms,
|
||||||
float r_scale[2])
|
float r_scale[2])
|
||||||
{
|
{
|
||||||
BoxPack *box_array = static_cast<BoxPack *>(
|
BoxPack *box_array = static_cast<BoxPack *>(
|
||||||
MEM_mallocN(sizeof(*box_array) * island_vector.size(), __func__));
|
MEM_mallocN(sizeof(*box_array) * island_vector.size(), __func__));
|
||||||
@ -351,4 +350,20 @@ BoxPack *pack_islands(const Span<PackIsland *> &island_vector,
|
|||||||
return box_array;
|
return box_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pack_islands(const Span<PackIsland *> &islands,
|
||||||
|
const UVPackIsland_Params ¶ms,
|
||||||
|
float r_scale[2])
|
||||||
|
{
|
||||||
|
BoxPack *box_array = pack_islands_box_array(islands, params, r_scale);
|
||||||
|
|
||||||
|
for (int64_t i : islands.index_range()) {
|
||||||
|
BoxPack *box = box_array + i;
|
||||||
|
PackIsland *island = islands[box->index];
|
||||||
|
island->pre_translate.x = box->x - island->bounds_rect.xmin;
|
||||||
|
island->pre_translate.y = box->y - island->bounds_rect.ymin;
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_freeN(box_array);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blender::geometry
|
} // namespace blender::geometry
|
||||||
|
@ -4167,6 +4167,7 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
geometry::PackIsland *pack_island = new geometry::PackIsland();
|
geometry::PackIsland *pack_island = new geometry::PackIsland();
|
||||||
|
pack_island->caller_index = i;
|
||||||
pack_island_vector.append(pack_island);
|
pack_island_vector.append(pack_island);
|
||||||
|
|
||||||
float minv[2];
|
float minv[2];
|
||||||
@ -4192,42 +4193,29 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
|
|||||||
params.udim_base_offset[1] = 0.0f;
|
params.udim_base_offset[1] = 0.0f;
|
||||||
|
|
||||||
float scale[2] = {1.0f, 1.0f};
|
float scale[2] = {1.0f, 1.0f};
|
||||||
BoxPack *box_array = pack_islands(pack_island_vector, params, scale);
|
pack_islands(pack_island_vector, params, scale);
|
||||||
|
|
||||||
for (int64_t i : pack_island_vector.index_range()) {
|
for (int64_t i : pack_island_vector.index_range()) {
|
||||||
BoxPack *box = box_array + i;
|
PackIsland *pack_island = pack_island_vector[i];
|
||||||
PackIsland *pack_island = pack_island_vector[box->index];
|
PChart *chart = handle->charts[pack_island->caller_index];
|
||||||
pack_island->bounds_rect.xmin = box->x - pack_island->bounds_rect.xmin;
|
|
||||||
pack_island->bounds_rect.ymin = box->y - pack_island->bounds_rect.ymin;
|
|
||||||
}
|
|
||||||
|
|
||||||
unpacked = 0;
|
|
||||||
for (int i = 0; i < handle->ncharts; i++) {
|
|
||||||
PChart *chart = handle->charts[i];
|
|
||||||
|
|
||||||
if (ignore_pinned && chart->has_pins) {
|
|
||||||
unpacked++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PackIsland *pack_island = pack_island_vector[i - unpacked];
|
|
||||||
|
|
||||||
/* TODO: Replace with #mul_v2_m2_add_v2v2 here soon. */
|
/* TODO: Replace with #mul_v2_m2_add_v2v2 here soon. */
|
||||||
float m[2];
|
float m[2];
|
||||||
float b[2];
|
float b[2];
|
||||||
m[0] = scale[0];
|
m[0] = scale[0];
|
||||||
m[1] = scale[1];
|
m[1] = scale[1];
|
||||||
b[0] = pack_island->bounds_rect.xmin;
|
b[0] = pack_island->pre_translate.x;
|
||||||
b[1] = pack_island->bounds_rect.ymin;
|
b[1] = pack_island->pre_translate.y;
|
||||||
for (PVert *v = chart->verts; v; v = v->nextlink) {
|
for (PVert *v = chart->verts; v; v = v->nextlink) {
|
||||||
/* Unusual accumulate-and-multiply here (will) reduce round-off error. */
|
/* Unusual accumulate-and-multiply here (will) reduce round-off error. */
|
||||||
v->uv[0] = m[0] * (v->uv[0] + b[0]);
|
v->uv[0] = m[0] * (v->uv[0] + b[0]);
|
||||||
v->uv[1] = m[1] * (v->uv[1] + b[1]);
|
v->uv[1] = m[1] * (v->uv[1] + b[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_island_vector[i - unpacked] = nullptr;
|
pack_island_vector[i] = nullptr;
|
||||||
delete pack_island;
|
delete pack_island;
|
||||||
}
|
}
|
||||||
MEM_freeN(box_array);
|
|
||||||
if (handle->aspx != handle->aspy) {
|
if (handle->aspx != handle->aspy) {
|
||||||
uv_parametrizer_scale(handle, handle->aspx, handle->aspy);
|
uv_parametrizer_scale(handle, handle->aspx, handle->aspy);
|
||||||
}
|
}
|
||||||
|
@ -341,4 +341,4 @@ void main(void)
|
|||||||
default:
|
default:
|
||||||
DISCARD_VERTEX
|
DISCARD_VERTEX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,4 +97,4 @@ TEST(std140, fl_vec2)
|
|||||||
EXPECT_EQ(offset, 16);
|
EXPECT_EQ(offset, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
@ -114,4 +114,4 @@ void VKDescriptorPools::free(VKDescriptorSet &descriptor_set)
|
|||||||
descriptor_set.mark_freed();
|
descriptor_set.mark_freed();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
@ -62,4 +62,4 @@ class VKDescriptorPools {
|
|||||||
bool is_last_pool_active();
|
bool is_last_pool_active();
|
||||||
void add_new_pool();
|
void add_new_pool();
|
||||||
};
|
};
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
@ -56,4 +56,4 @@ void VKStateManager::texture_unpack_row_length_set(uint /*len*/)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
@ -27,4 +27,4 @@ class VKStateManager : public StateManager {
|
|||||||
|
|
||||||
void texture_unpack_row_length_set(uint len) override;
|
void texture_unpack_row_length_set(uint len) override;
|
||||||
};
|
};
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
@ -10,10 +10,10 @@ set(INC
|
|||||||
../../blenlib
|
../../blenlib
|
||||||
../../bmesh
|
../../bmesh
|
||||||
../../depsgraph
|
../../depsgraph
|
||||||
|
../../geometry
|
||||||
../../makesdna
|
../../makesdna
|
||||||
../../makesrna
|
../../makesrna
|
||||||
../../windowmanager
|
../../windowmanager
|
||||||
../../geometry
|
|
||||||
../../../../extern/fmtlib/include
|
../../../../extern/fmtlib/include
|
||||||
../../../../intern/guardedalloc
|
../../../../intern/guardedalloc
|
||||||
)
|
)
|
||||||
@ -23,37 +23,37 @@ set(INC_SYS
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(SRC
|
set(SRC
|
||||||
|
exporter/ply_export.cc
|
||||||
exporter/ply_export_data.cc
|
exporter/ply_export_data.cc
|
||||||
exporter/ply_export_header.cc
|
exporter/ply_export_header.cc
|
||||||
exporter/ply_export_load_plydata.cc
|
exporter/ply_export_load_plydata.cc
|
||||||
exporter/ply_export.cc
|
exporter/ply_file_buffer.cc
|
||||||
exporter/ply_file_buffer_ascii.cc
|
exporter/ply_file_buffer_ascii.cc
|
||||||
exporter/ply_file_buffer_binary.cc
|
exporter/ply_file_buffer_binary.cc
|
||||||
exporter/ply_file_buffer.cc
|
importer/ply_import.cc
|
||||||
importer/ply_import_ascii.cc
|
importer/ply_import_ascii.cc
|
||||||
importer/ply_import_binary.cc
|
importer/ply_import_binary.cc
|
||||||
importer/ply_import_mesh.cc
|
importer/ply_import_mesh.cc
|
||||||
importer/ply_import.cc
|
|
||||||
IO_ply.cc
|
IO_ply.cc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exporter/ply_export.hh
|
||||||
exporter/ply_export_data.hh
|
exporter/ply_export_data.hh
|
||||||
exporter/ply_export_header.hh
|
exporter/ply_export_header.hh
|
||||||
exporter/ply_export_load_plydata.hh
|
exporter/ply_export_load_plydata.hh
|
||||||
exporter/ply_export.hh
|
exporter/ply_file_buffer.hh
|
||||||
exporter/ply_file_buffer_ascii.hh
|
exporter/ply_file_buffer_ascii.hh
|
||||||
exporter/ply_file_buffer_binary.hh
|
exporter/ply_file_buffer_binary.hh
|
||||||
exporter/ply_file_buffer.hh
|
importer/ply_import.hh
|
||||||
importer/ply_import_ascii.hh
|
importer/ply_import_ascii.hh
|
||||||
importer/ply_import_binary.hh
|
importer/ply_import_binary.hh
|
||||||
importer/ply_import_mesh.hh
|
importer/ply_import_mesh.hh
|
||||||
importer/ply_import.hh
|
|
||||||
IO_ply.h
|
IO_ply.h
|
||||||
|
|
||||||
intern/ply_data.hh
|
intern/ply_data.hh
|
||||||
intern/ply_functions.hh
|
|
||||||
intern/ply_functions.cc
|
intern/ply_functions.cc
|
||||||
|
intern/ply_functions.hh
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIB
|
set(LIB
|
||||||
|
@ -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)
|
||||||
|
@ -54,11 +54,11 @@ static void rotate_instances(GeoNodeExecParams ¶ms, bke::Instances &instance
|
|||||||
/* Create rotations around the individual axis. This could be optimized to skip some axis
|
/* Create rotations around the individual axis. This could be optimized to skip some axis
|
||||||
* when the angle is zero. */
|
* when the angle is zero. */
|
||||||
const float3x3 rotation_x = from_rotation<float3x3>(
|
const float3x3 rotation_x = from_rotation<float3x3>(
|
||||||
AxisAngle(instance_transform.x_axis(), euler.x));
|
AxisAngle(normalize(instance_transform.x_axis()), euler.x));
|
||||||
const float3x3 rotation_y = from_rotation<float3x3>(
|
const float3x3 rotation_y = from_rotation<float3x3>(
|
||||||
AxisAngle(instance_transform.y_axis(), euler.y));
|
AxisAngle(normalize(instance_transform.y_axis()), euler.y));
|
||||||
const float3x3 rotation_z = from_rotation<float3x3>(
|
const float3x3 rotation_z = from_rotation<float3x3>(
|
||||||
AxisAngle(instance_transform.z_axis(), euler.z));
|
AxisAngle(normalize(instance_transform.z_axis()), euler.z));
|
||||||
|
|
||||||
/* Combine the previously computed rotations into the final rotation matrix. */
|
/* Combine the previously computed rotations into the final rotation matrix. */
|
||||||
rotation_matrix = float4x4(rotation_z * rotation_y * rotation_x);
|
rotation_matrix = float4x4(rotation_z * rotation_y * rotation_x);
|
||||||
|
@ -141,6 +141,7 @@ dict_custom = {
|
|||||||
"instantiable",
|
"instantiable",
|
||||||
"instantiation",
|
"instantiation",
|
||||||
"instantiations",
|
"instantiations",
|
||||||
|
"interdependencies",
|
||||||
"interferences",
|
"interferences",
|
||||||
"interocular",
|
"interocular",
|
||||||
"invariant",
|
"invariant",
|
||||||
|
Loading…
Reference in New Issue
Block a user