- Optionally get the error as a single line. - Support access the error as an allocated string. - PyC_ExceptionBuffer_Simple was always printing to the `stdout` while PyC_ExceptionBuffer didn't, now either print to the output. Without this, callers are unable to do anything with the error string.
636 lines
18 KiB
C
636 lines
18 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup edutil
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_string.h"
|
|
#include "BLI_string_cursor_utf8.h"
|
|
#include "BLI_string_utf8.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_unit.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#ifdef WITH_PYTHON
|
|
# include "BPY_extern_run.h"
|
|
#endif
|
|
|
|
#include "ED_numinput.h"
|
|
#include "UI_interface.h"
|
|
|
|
/* Numeric input which isn't allowing full numeric editing. */
|
|
#define USE_FAKE_EDIT
|
|
|
|
/**
|
|
* #NumInput.flag
|
|
* (1 << 8) and below are reserved for public flags!
|
|
*/
|
|
enum {
|
|
/** Enable full editing, with units and math operators support. */
|
|
NUM_EDIT_FULL = (1 << 9),
|
|
#ifdef USE_FAKE_EDIT
|
|
/** Fake edited state (temp, avoids issue with backspace). */
|
|
NUM_FAKE_EDITED = (1 << 10),
|
|
#endif
|
|
};
|
|
|
|
/* NumInput.val_flag[] */
|
|
enum {
|
|
/* (1 << 8) and below are reserved for public flags! */
|
|
/** User has edited this value somehow. */
|
|
NUM_EDITED = (1 << 9),
|
|
/** Current expression for this value is invalid. */
|
|
NUM_INVALID = (1 << 10),
|
|
#ifdef USE_FAKE_EDIT
|
|
/** Current expression's result has to be negated. */
|
|
NUM_NEGATE = (1 << 11),
|
|
/** Current expression's result has to be inverted. */
|
|
NUM_INVERSE = (1 << 12),
|
|
#endif
|
|
};
|
|
|
|
/* ************************** Functions *************************** */
|
|
|
|
/* ************************** NUMINPUT **************************** */
|
|
|
|
void initNumInput(NumInput *n)
|
|
{
|
|
n->idx_max = 0;
|
|
n->unit_sys = USER_UNIT_NONE;
|
|
copy_vn_i(n->unit_type, NUM_MAX_ELEMENTS, B_UNIT_NONE);
|
|
n->unit_use_radians = false;
|
|
|
|
n->flag = 0;
|
|
copy_vn_short(n->val_flag, NUM_MAX_ELEMENTS, 0);
|
|
zero_v3(n->val);
|
|
copy_vn_fl(n->val_org, NUM_MAX_ELEMENTS, 0.0f);
|
|
copy_vn_fl(n->val_inc, NUM_MAX_ELEMENTS, 1.0f);
|
|
|
|
n->idx = 0;
|
|
n->str[0] = '\0';
|
|
n->str_cur = 0;
|
|
}
|
|
|
|
/* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */
|
|
void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings)
|
|
{
|
|
short j;
|
|
const int ln = NUM_STR_REP_LEN;
|
|
int prec = 2; /* draw-only, and avoids too much issues with radian->degrees conversion. */
|
|
|
|
for (j = 0; j <= n->idx_max; j++) {
|
|
/* if AFFECTALL and no number typed and cursor not on number, use first number */
|
|
const short i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ?
|
|
0 :
|
|
j;
|
|
|
|
/* Use scale_length if needed! */
|
|
const float fac = (float)BKE_scene_unit_scale(unit_settings, n->unit_type[j], 1.0);
|
|
|
|
if (n->val_flag[i] & NUM_EDITED) {
|
|
/* Get the best precision, allows us to draw '10.0001' as '10' instead! */
|
|
prec = UI_calc_float_precision(prec, (double)n->val[i]);
|
|
if (i == n->idx) {
|
|
const char *heading_exp = "", *trailing_exp = "";
|
|
char before_cursor[NUM_STR_REP_LEN];
|
|
char val[16];
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
if (n->val_flag[i] & NUM_NEGATE) {
|
|
heading_exp = (n->val_flag[i] & NUM_INVERSE) ? "-1/(" : "-(";
|
|
trailing_exp = ")";
|
|
}
|
|
else if (n->val_flag[i] & NUM_INVERSE) {
|
|
heading_exp = "1/(";
|
|
trailing_exp = ")";
|
|
}
|
|
#endif
|
|
|
|
if (n->val_flag[i] & NUM_INVALID) {
|
|
BLI_strncpy(val, "Invalid", sizeof(val));
|
|
}
|
|
else {
|
|
BKE_unit_value_as_string_adaptive(val,
|
|
sizeof(val),
|
|
(double)(n->val[i] * fac),
|
|
prec,
|
|
n->unit_sys,
|
|
n->unit_type[i],
|
|
true,
|
|
false);
|
|
}
|
|
|
|
/* +1 because of trailing '\0' */
|
|
BLI_strncpy(before_cursor, n->str, n->str_cur + 1);
|
|
BLI_snprintf(&str[j * ln],
|
|
ln,
|
|
"[%s%s|%s%s] = %s",
|
|
heading_exp,
|
|
before_cursor,
|
|
&n->str[n->str_cur],
|
|
trailing_exp,
|
|
val);
|
|
}
|
|
else {
|
|
const char *cur = (i == n->idx) ? "|" : "";
|
|
if (n->unit_use_radians && n->unit_type[i] == B_UNIT_ROTATION) {
|
|
/* Radian exception... */
|
|
BLI_snprintf(&str[j * ln], ln, "%s%.6gr%s", cur, n->val[i], cur);
|
|
}
|
|
else {
|
|
char tstr[NUM_STR_REP_LEN];
|
|
BKE_unit_value_as_string_adaptive(
|
|
tstr, ln, (double)n->val[i], prec, n->unit_sys, n->unit_type[i], true, false);
|
|
BLI_snprintf(&str[j * ln], ln, "%s%s%s", cur, tstr, cur);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
const char *cur = (i == n->idx) ? "|" : "";
|
|
BLI_snprintf(&str[j * ln], ln, "%sNONE%s", cur, cur);
|
|
}
|
|
/* We might have cut some multi-bytes utf8 chars
|
|
* (e.g. trailing '°' of degrees values can become only 'A')... */
|
|
BLI_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln]));
|
|
}
|
|
}
|
|
|
|
bool hasNumInput(const NumInput *n)
|
|
{
|
|
short i;
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
if (n->flag & NUM_FAKE_EDITED) {
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i <= n->idx_max; i++) {
|
|
if (n->val_flag[i] & NUM_EDITED) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \warning \a vec must be set beforehand otherwise we risk uninitialized vars.
|
|
*/
|
|
bool applyNumInput(NumInput *n, float *vec)
|
|
{
|
|
short i, j;
|
|
float val;
|
|
|
|
if (hasNumInput(n)) {
|
|
for (j = 0; j <= n->idx_max; j++) {
|
|
#ifdef USE_FAKE_EDIT
|
|
if (n->flag & NUM_FAKE_EDITED) {
|
|
val = n->val[j];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* if AFFECTALL and no number typed and cursor not on number, use first number */
|
|
i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
|
|
val = (!(n->val_flag[i] & NUM_EDITED) && n->val_flag[i] & NUM_NULL_ONE) ? 1.0f : n->val[i];
|
|
|
|
if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) {
|
|
val = 0.0f;
|
|
}
|
|
if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) {
|
|
val = floorf(val + 0.5f);
|
|
if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
|
|
val = 1.0f;
|
|
}
|
|
}
|
|
else if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
|
|
val = 0.0001f;
|
|
}
|
|
}
|
|
vec[j] = val;
|
|
}
|
|
#ifdef USE_FAKE_EDIT
|
|
n->flag &= ~NUM_FAKE_EDITED;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
/* Else, we set the 'org' values for numinput! */
|
|
for (j = 0; j <= n->idx_max; j++) {
|
|
n->val[j] = n->val_org[j] = vec[j];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void value_to_editstr(NumInput *n, int idx)
|
|
{
|
|
const int prec = 6; /* editing, higher precision needed. */
|
|
n->str_cur = BKE_unit_value_as_string_adaptive(n->str,
|
|
NUM_STR_REP_LEN,
|
|
(double)n->val[idx],
|
|
prec,
|
|
n->unit_sys,
|
|
n->unit_type[idx],
|
|
true,
|
|
false);
|
|
}
|
|
|
|
static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf_len)
|
|
{
|
|
int cur = n->str_cur;
|
|
int len = strlen(&n->str[cur]) + 1; /* +1 for the trailing '\0'. */
|
|
int n_cur = cur + buf_len;
|
|
|
|
if (n_cur + len >= NUM_STR_REP_LEN) {
|
|
return false;
|
|
}
|
|
|
|
memmove(&n->str[n_cur], &n->str[cur], len);
|
|
memcpy(&n->str[cur], buf, sizeof(char) * buf_len);
|
|
|
|
n->str_cur = n_cur;
|
|
return true;
|
|
}
|
|
|
|
bool user_string_to_number(bContext *C,
|
|
const char *str,
|
|
const UnitSettings *unit,
|
|
int type,
|
|
double *r_value,
|
|
const bool use_single_line_error,
|
|
char **r_error)
|
|
{
|
|
#ifdef WITH_PYTHON
|
|
struct BPy_RunErrInfo err_info = {
|
|
.use_single_line_error = use_single_line_error,
|
|
.r_string = r_error,
|
|
};
|
|
double unit_scale = BKE_scene_unit_scale(unit, type, 1.0);
|
|
if (BKE_unit_string_contains_unit(str, type)) {
|
|
char str_unit_convert[256];
|
|
BLI_strncpy(str_unit_convert, str, sizeof(str_unit_convert));
|
|
BKE_unit_replace_string(
|
|
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
|
|
|
|
return BPY_run_string_as_number(C, NULL, str_unit_convert, &err_info, r_value);
|
|
}
|
|
|
|
int success = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
|
|
*r_value = BKE_unit_apply_preferred_unit(unit, type, *r_value);
|
|
*r_value /= unit_scale;
|
|
return success;
|
|
|
|
#else
|
|
UNUSED_VARS(C, unit, type);
|
|
*r_value = atof(str);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
static bool editstr_is_simple_numinput(const char ascii)
|
|
{
|
|
if (ascii >= '0' && ascii <= '9') {
|
|
return true;
|
|
}
|
|
if (ascii == '.') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
|
|
{
|
|
const char *utf8_buf = NULL;
|
|
char ascii[2] = {'\0', '\0'};
|
|
bool updated = false;
|
|
short idx = n->idx, idx_max = n->idx_max;
|
|
short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
|
|
int cur;
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
if (U.flag & USER_FLAG_NUMINPUT_ADVANCED)
|
|
#endif
|
|
{
|
|
if ((event->ctrl == 0) && (event->alt == 0) && (event->ascii != '\0') &&
|
|
strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) {
|
|
if (!(n->flag & NUM_EDIT_FULL)) {
|
|
n->flag |= NUM_EDITED;
|
|
n->flag |= NUM_EDIT_FULL;
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
/* XXX Hack around keyboards without direct access to '=' nor '*'... */
|
|
if (ELEM(event->ascii, '=', '*')) {
|
|
if (!(n->flag & NUM_EDIT_FULL)) {
|
|
n->flag |= NUM_EDIT_FULL;
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
return true;
|
|
}
|
|
if (event->ctrl) {
|
|
n->flag &= ~NUM_EDIT_FULL;
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
switch (event->type) {
|
|
case EVT_MODAL_MAP:
|
|
if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
|
|
n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
|
|
value_to_editstr(n, idx);
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
updated = true;
|
|
}
|
|
else {
|
|
/* might be a char too... */
|
|
utf8_buf = event->utf8_buf;
|
|
ascii[0] = event->ascii;
|
|
}
|
|
break;
|
|
case EVT_BACKSPACEKEY:
|
|
/* Part specific to backspace... */
|
|
if (!(n->val_flag[idx] & NUM_EDITED)) {
|
|
copy_v3_v3(n->val, n->val_org);
|
|
n->val_flag[0] &= ~NUM_EDITED;
|
|
n->val_flag[1] &= ~NUM_EDITED;
|
|
n->val_flag[2] &= ~NUM_EDITED;
|
|
#ifdef USE_FAKE_EDIT
|
|
n->flag |= NUM_FAKE_EDITED;
|
|
#else
|
|
n->flag |= NUM_EDIT_FULL;
|
|
#endif
|
|
updated = true;
|
|
break;
|
|
}
|
|
else if (event->shift || !n->str[0]) {
|
|
n->val[idx] = n->val_org[idx];
|
|
n->val_flag[idx] &= ~NUM_EDITED;
|
|
n->str[0] = '\0';
|
|
n->str_cur = 0;
|
|
updated = true;
|
|
break;
|
|
}
|
|
/* Else, common behavior with DELKEY,
|
|
* only difference is remove char(s) before/after the cursor. */
|
|
dir = STRCUR_DIR_PREV;
|
|
ATTR_FALLTHROUGH;
|
|
case EVT_DELKEY:
|
|
if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
|
|
int t_cur = cur = n->str_cur;
|
|
if (event->ctrl) {
|
|
mode = STRCUR_JUMP_DELIM;
|
|
}
|
|
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
|
|
if (t_cur != cur) {
|
|
if (t_cur < cur) {
|
|
SWAP(int, t_cur, cur);
|
|
n->str_cur = cur;
|
|
}
|
|
/* +1 for trailing '\0'. */
|
|
memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);
|
|
updated = true;
|
|
}
|
|
if (!n->str[0]) {
|
|
n->val[idx] = n->val_org[idx];
|
|
}
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
break;
|
|
case EVT_LEFTARROWKEY:
|
|
dir = STRCUR_DIR_PREV;
|
|
ATTR_FALLTHROUGH;
|
|
case EVT_RIGHTARROWKEY:
|
|
cur = n->str_cur;
|
|
if (event->ctrl) {
|
|
mode = STRCUR_JUMP_DELIM;
|
|
}
|
|
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
|
|
if (cur != n->str_cur) {
|
|
n->str_cur = cur;
|
|
return true;
|
|
}
|
|
return false;
|
|
case EVT_HOMEKEY:
|
|
if (n->str[0]) {
|
|
n->str_cur = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
case EVT_ENDKEY:
|
|
if (n->str[0]) {
|
|
n->str_cur = strlen(n->str);
|
|
return true;
|
|
}
|
|
return false;
|
|
case EVT_TABKEY:
|
|
#ifdef USE_FAKE_EDIT
|
|
n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);
|
|
#endif
|
|
|
|
idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
|
|
n->idx = idx;
|
|
if (n->val_flag[idx] & NUM_EDITED) {
|
|
value_to_editstr(n, idx);
|
|
}
|
|
else {
|
|
n->str[0] = '\0';
|
|
n->str_cur = 0;
|
|
}
|
|
return true;
|
|
case EVT_PADPERIOD:
|
|
case EVT_PERIODKEY:
|
|
/* Force numdot, some OSs/countries generate a comma char in this case,
|
|
* sic... (T37992) */
|
|
ascii[0] = '.';
|
|
utf8_buf = ascii;
|
|
break;
|
|
#if 0
|
|
/* Those keys are not directly accessible in all layouts,
|
|
* preventing to generate matching events.
|
|
* So we use a hack (ascii value) instead, see below.
|
|
*/
|
|
case EQUALKEY:
|
|
case PADASTERKEY:
|
|
if (!(n->flag & NUM_EDIT_FULL)) {
|
|
n->flag |= NUM_EDIT_FULL;
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
return true;
|
|
}
|
|
else if (event->ctrl) {
|
|
n->flag &= ~NUM_EDIT_FULL;
|
|
return true;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
case EVT_PADMINUS:
|
|
case EVT_MINUSKEY:
|
|
if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
|
|
n->val_flag[idx] ^= NUM_NEGATE;
|
|
updated = true;
|
|
}
|
|
break;
|
|
case EVT_PADSLASHKEY:
|
|
case EVT_SLASHKEY:
|
|
if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
|
|
n->val_flag[idx] ^= NUM_INVERSE;
|
|
updated = true;
|
|
}
|
|
break;
|
|
#endif
|
|
case EVT_CKEY:
|
|
if (event->ctrl) {
|
|
/* Copy current `str` to the copy/paste buffer. */
|
|
WM_clipboard_text_set(n->str, 0);
|
|
updated = true;
|
|
}
|
|
break;
|
|
case EVT_VKEY:
|
|
if (event->ctrl) {
|
|
/* extract the first line from the clipboard */
|
|
int pbuf_len;
|
|
char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);
|
|
|
|
if (pbuf) {
|
|
const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len);
|
|
|
|
MEM_freeN(pbuf);
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
}
|
|
updated = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
|
|
utf8_buf = event->utf8_buf;
|
|
ascii[0] = event->ascii;
|
|
}
|
|
|
|
/* Up to this point, if we have a ctrl modifier, skip.
|
|
* This allows to still access most of modals' shortcuts even in numinput mode.
|
|
*/
|
|
if (!updated && event->ctrl) {
|
|
return false;
|
|
}
|
|
|
|
if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) {
|
|
/* Fallback to ascii. */
|
|
utf8_buf = ascii;
|
|
}
|
|
|
|
if (utf8_buf && utf8_buf[0]) {
|
|
if (!(n->flag & NUM_EDIT_FULL)) {
|
|
/* In simple edit mode, we only keep a few chars as valid! */
|
|
/* no need to decode unicode, ascii is first char only */
|
|
if (!editstr_is_simple_numinput(utf8_buf[0])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
|
|
return false;
|
|
}
|
|
|
|
n->val_flag[idx] |= NUM_EDITED;
|
|
}
|
|
else if (!updated) {
|
|
return false;
|
|
}
|
|
|
|
/* At this point, our value has changed, try to interpret it with python
|
|
* (if str is not empty!). */
|
|
if (n->str[0]) {
|
|
const float val_prev = n->val[idx];
|
|
Scene *sce = CTX_data_scene(C);
|
|
char *error = NULL;
|
|
|
|
double val;
|
|
int success = user_string_to_number(
|
|
C, n->str, &sce->unit, n->unit_type[idx], &val, false, &error);
|
|
|
|
if (error) {
|
|
ReportList *reports = CTX_wm_reports(C);
|
|
printf("%s\n", error);
|
|
BKE_report(reports, RPT_ERROR, error);
|
|
BKE_report(reports, RPT_ERROR, IFACE_("Numeric input evaluation"));
|
|
MEM_freeN(error);
|
|
}
|
|
|
|
if (success) {
|
|
n->val[idx] = (float)val;
|
|
n->val_flag[idx] &= ~NUM_INVALID;
|
|
}
|
|
else {
|
|
n->val_flag[idx] |= NUM_INVALID;
|
|
}
|
|
|
|
#ifdef USE_FAKE_EDIT
|
|
if (n->val_flag[idx] & NUM_NEGATE) {
|
|
n->val[idx] = -n->val[idx];
|
|
}
|
|
if (n->val_flag[idx] & NUM_INVERSE) {
|
|
val = n->val[idx];
|
|
/* If we invert on radians when user is in degrees,
|
|
* you get unexpected results... See T53463. */
|
|
if (!n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) {
|
|
val = RAD2DEG(val);
|
|
}
|
|
val = 1.0 / val;
|
|
if (!n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) {
|
|
val = DEG2RAD(val);
|
|
}
|
|
n->val[idx] = (float)val;
|
|
}
|
|
#endif
|
|
|
|
if (UNLIKELY(!isfinite(n->val[idx]))) {
|
|
n->val[idx] = val_prev;
|
|
n->val_flag[idx] |= NUM_INVALID;
|
|
}
|
|
}
|
|
|
|
/* REDRAW SINCE NUMBERS HAVE CHANGED */
|
|
return true;
|
|
}
|