text: /*
   * 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.
   *
   * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
   * All rights reserved.
   */
  
  /** \file
   * \ingroup bke
   */
  
  #include "MEM_guardedalloc.h"
  
  #include "DNA_anim_types.h"
  #include "DNA_constraint_types.h"
  #include "DNA_object_types.h"
  
  #include "BLI_alloca.h"
  #include "BLI_expr_pylike_eval.h"
  #include "BLI_math.h"
  #include "BLI_string_utils.h"
  #include "BLI_threads.h"
  #include "BLI_utildefines.h"
  
  #include "BLT_translation.h"
  
  #include "BKE_action.h"
  #include "BKE_animsys.h"
  #include "BKE_armature.h"
  #include "BKE_constraint.h"
  #include "BKE_fcurve_driver.h"
  #include "BKE_global.h"
  #include "BKE_object.h"
  
  #include "RNA_access.h"
  
  #include "atomic_ops.h"
  
  #include "CLG_log.h"
  
  #ifdef WITH_PYTHON
  #  include "BPY_extern.h"
  #endif
  
  #ifdef WITH_PYTHON
  static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER;
  #endif
  
  static CLG_LogRef LOG = {"bke.fcurve"};
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Variables
   * \{ */
  
  /* TypeInfo for Driver Variables (dvti) */
  typedef struct DriverVarTypeInfo {
    /* Evaluation callback. */
    float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
  
    /* Allocation of target slots. */
    int num_targets;                              /* Number of target slots required. */
    const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots. */
    short target_flags[MAX_DRIVER_TARGETS]; /* Flags defining the requirements for each slot. */
  } DriverVarTypeInfo;
  
  /* Macro to begin definitions */
  #define BEGIN_DVAR_TYPEDEF(type) {
  
  /* Macro to end definitions */
  #define END_DVAR_TYPEDEF }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Target Utilities
   * \{ */
  
  static ID *dtar_id_ensure_proxy_from(ID *id)
  {
    if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) {
      return (ID *)(((Object *)id)->proxy_from);
    }
    return id;
  }
  
  /**
   * Helper function to obtain a value using RNA from the specified source
   * (for evaluating drivers).
   */
  static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
  {
    PointerRNA id_ptr, ptr;
    PropertyRNA *prop;
    ID *id;
    int index = -1;
    float value = 0.0f;
  
    /* Sanity check. */
    if (ELEM(NULL, driver, dtar)) {
      return 0.0f;
    }
  
    id = dtar_id_ensure_proxy_from(dtar->id);
  
    /* Error check for missing pointer. */
    if (id == NULL) {
      if (G.debug & G_DEBUG) {
        CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
      }
  
      driver->flag |= DRIVER_FLAG_INVALID;
      dtar->flag |= DTAR_FLAG_INVALID;
      return 0.0f;
    }
  
    /* Get RNA-pointer for the ID-block given in target. */
    RNA_id_pointer_create(id, &id_ptr);
  
    /* Get property to read from, and get value as appropriate. */
    if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
      /* Path couldn't be resolved. */
      if (G.debug & G_DEBUG) {
        CLOG_ERROR(&LOG,
                   "Driver Evaluation Error: cannot resolve target for %s -> %s",
                   id->name,
                   dtar->rna_path);
      }
  
      driver->flag |= DRIVER_FLAG_INVALID;
      dtar->flag |= DTAR_FLAG_INVALID;
      return 0.0f;
    }
  
    if (RNA_property_array_check(prop)) {
      /* Array. */
      if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
        /* Out of bounds. */
        if (G.debug & G_DEBUG) {
          CLOG_ERROR(&LOG,
                     "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
                     id->name,
                     dtar->rna_path,
                     index);
        }
  
        driver->flag |= DRIVER_FLAG_INVALID;
        dtar->flag |= DTAR_FLAG_INVALID;
        return 0.0f;
      }
  
      switch (RNA_property_type(prop)) {
        case PROP_BOOLEAN:
          value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
          break;
        case PROP_INT:
          value = (float)RNA_property_int_get_index(&ptr, prop, index);
          break;
        case PROP_FLOAT:
          value = RNA_property_float_get_index(&ptr, prop, index);
          break;
        default:
          break;
      }
    }
    else {
      /* Not an array. */
      switch (RNA_property_type(prop)) {
        case PROP_BOOLEAN:
          value = (float)RNA_property_boolean_get(&ptr, prop);
          break;
        case PROP_INT:
          value = (float)RNA_property_int_get(&ptr, prop);
          break;
        case PROP_FLOAT:
          value = RNA_property_float_get(&ptr, prop);
          break;
        case PROP_ENUM:
          value = (float)RNA_property_enum_get(&ptr, prop);
          break;
        default:
          break;
      }
    }
  
    /* If we're still here, we should be ok. */
    dtar->flag &= ~DTAR_FLAG_INVALID;
    return value;
  }
  
  /**
   * Same as 'dtar_get_prop_val'. but get the RNA property.
   */
  bool driver_get_variable_property(ChannelDriver *driver,
                                    DriverTarget *dtar,
                                    PointerRNA *r_ptr,
                                    PropertyRNA **r_prop,
                                    int *r_index)
  {
    PointerRNA id_ptr;
    PointerRNA ptr;
    PropertyRNA *prop;
    ID *id;
    int index = -1;
  
    /* Sanity check. */
    if (ELEM(NULL, driver, dtar)) {
      return false;
    }
  
    id = dtar_id_ensure_proxy_from(dtar->id);
  
    /* Error check for missing pointer. */
    if (id == NULL) {
      if (G.debug & G_DEBUG) {
        CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
      }
  
      driver->flag |= DRIVER_FLAG_INVALID;
      dtar->flag |= DTAR_FLAG_INVALID;
      return false;
    }
  
    /* Get RNA-pointer for the ID-block given in target. */
    RNA_id_pointer_create(id, &id_ptr);
  
    /* Get property to read from, and get value as appropriate. */
    if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
      ptr = PointerRNA_NULL;
      prop = NULL; /* OK. */
    }
    else if (RNA_path_resolve_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
      /* OK. */
    }
    else {
      /* Path couldn't be resolved. */
      if (G.debug & G_DEBUG) {
        CLOG_ERROR(&LOG,
                   "Driver Evaluation Error: cannot resolve target for %s -> %s",
                   id->name,
                   dtar->rna_path);
      }
  
      ptr = PointerRNA_NULL;
      *r_prop = NULL;
      *r_index = -1;
  
      driver->flag |= DRIVER_FLAG_INVALID;
      dtar->flag |= DTAR_FLAG_INVALID;
      return false;
    }
  
    *r_ptr = ptr;
    *r_prop = prop;
    *r_index = index;
  
    /* If we're still here, we should be ok. */
    dtar->flag &= ~DTAR_FLAG_INVALID;
    return true;
  }
  
  static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
  {
    short valid_targets = 0;
  
    DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
      Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
  
      /* Check if this target has valid data. */
      if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
        /* Invalid target, so will not have enough targets. */
        driver->flag |= DRIVER_FLAG_INVALID;
        dtar->flag |= DTAR_FLAG_INVALID;
      }
      else {
        /* Target seems to be OK now. */
        dtar->flag &= ~DTAR_FLAG_INVALID;
        valid_targets++;
      }
    }
    DRIVER_TARGETS_LOOPER_END;
  
    return valid_targets;
  }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Variable Utilities
   * \{ */
  
  /* Evaluate 'single prop' driver variable. */
  static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
  {
    /* Just evaluate the first target slot. */
    return dtar_get_prop_val(driver, &dvar->targets[0]);
  }
  
  /* Evaluate 'rotation difference' driver variable. */
  static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
  {
    short valid_targets = driver_check_valid_targets(driver, dvar);
  
    /* Make sure we have enough valid targets to use - all or nothing for now. */
    if (driver_check_valid_targets(driver, dvar) != 2) {
      if (G.debug & G_DEBUG) {
        CLOG_WARN(&LOG,
                  "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
                  valid_targets,
                  dvar->targets[0].id,
                  dvar->targets[1].id);
      }
      return 0.0f;
    }
  
    float(*mat[2])[4];
  
    /* NOTE: for now, these are all just worldspace */
    for (int i = 0; i < 2; i++) {
      /* Get pointer to loc values to store in. */
      DriverTarget *dtar = &dvar->targets[i];
      Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
      bPoseChannel *pchan;
  
      /* After the checks above, the targets should be valid here. */
      BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
  
      /* Try to get pose-channel. */
      pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
  
      /* Check if object or bone. */
      if (pchan) {
        /* Bone. */
        mat[i] = pchan->pose_mat;
      }
      else {
        /* Object. */
        mat[i] = ob->obmat;
      }
    }
  
    float q1[4], q2[4], quat[4], angle;
  
    /* Use the final posed locations. */
    mat4_to_quat(q1, mat[0]);
    mat4_to_quat(q2, mat[1]);
  
    invert_qt_normalized(q1);
    mul_qt_qtqt(quat, q1, q2);
    angle = 2.0f * (saacos(quat[0]));
    angle = fabsf(angle);
  
    return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle);
  }
  
  /**
   * Evaluate 'location difference' driver variable.
   *
   * TODO: this needs to take into account space conversions.
   */
  static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
  {
    float loc1[3] = {0.0f, 0.0f, 0.0f};
    float loc2[3] = {0.0f, 0.0f, 0.0f};
    short valid_targets = driver_check_valid_targets(driver, dvar);
  
    /* Make sure we have enough valid targets to use - all or nothing for now. */
    if (valid_targets < dvar->num_targets) {
      if (G.debug & G_DEBUG) {
        CLOG_WARN(&LOG,
                  "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
                  valid_targets,
                  dvar->targets[0].id,
                  dvar->targets[1].id);
      }
      return 0.0f;
    }
  
    /* SECOND PASS: get two location values */
    /* NOTE: for now, these are all just world-space */
    DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
      /* Get pointer to loc values to store in. */
      Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
      bPoseChannel *pchan;
      float tmp_loc[3];
  
      /* After the checks above, the targets should be valid here. */
      BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
  
      /* Try to get pose-channel. */
      pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
  
      /* Check if object or bone. */
      if (pchan) {
        /* Bone. */
        if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
          if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
            float mat[4][4];
  
            /* Extract transform just like how the constraints do it! */
            copy_m4_m4(mat, pchan->pose_mat);
            BKE_constraint_mat_convertspace(
                ob, pchan, NULL, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
  
            /* ... and from that, we get our transform. */
            copy_v3_v3(tmp_loc, mat[3]);
          }
          else {
            /* Transform space (use transform values directly). */
            copy_v3_v3(tmp_loc, pchan->loc);
          }
        }
        else {
          /* Convert to worldspace. */
          copy_v3_v3(tmp_loc, pchan->pose_head);
          mul_m4_v3(ob->obmat, tmp_loc);
        }
      }
      else {
        /* Object. */
        if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
          if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
            /* XXX: this should practically be the same as transform space. */
            float mat[4][4];
  
            /* Extract transform just like how the constraints do it! */
            copy_m4_m4(mat, ob->obmat);
            BKE_constraint_mat_convertspace(
                ob, NULL, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
  
            /* ... and from that, we get our transform. */
            copy_v3_v3(tmp_loc, mat[3]);
          }
          else {
            /* Transform space (use transform values directly). */
            copy_v3_v3(tmp_loc, ob->loc);
          }
        }
        else {
          /* World-space. */
          copy_v3_v3(tmp_loc, ob->obmat[3]);
        }
      }
  
      /* Copy the location to the right place. */
      if (tarIndex) {
        copy_v3_v3(loc2, tmp_loc);
      }
      else {
        copy_v3_v3(loc1, tmp_loc);
      }
    }
    DRIVER_TARGETS_LOOPER_END;
  
    /* If we're still here, there should now be two targets to use,
     * so just take the length of the vector between these points. */
    return len_v3v3(loc1, loc2);
  }
  
  /**
   * Evaluate 'transform channel' driver variable.
   */
  static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
  {
    DriverTarget *dtar = &dvar->targets[0];
    Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
    bPoseChannel *pchan;
    float mat[4][4];
    float oldEul[3] = {0.0f, 0.0f, 0.0f};
    bool use_eulers = false;
    short rot_order = ROT_MODE_EUL;
  
    /* Check if this target has valid data. */
    if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
      /* Invalid target, so will not have enough targets. */
      driver->flag |= DRIVER_FLAG_INVALID;
      dtar->flag |= DTAR_FLAG_INVALID;
      return 0.0f;
    }
  
    /* Target should be valid now. */
    dtar->flag &= ~DTAR_FLAG_INVALID;
  
    /* Try to get pose-channel. */
    pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
  
    /* Check if object or bone, and get transform matrix accordingly:
     * - "use_eulers" code is used to prevent the problems associated with non-uniqueness
     *   of euler decomposition from matrices T20870.
     * - "local-space" is for T21384, where parent results are not wanted
     *   but #DTAR_FLAG_LOCAL_CONSTS is for all the common "corrective-shapes-for-limbs" situations.
     */
    if (pchan) {
      /* Bone. */
      if (pchan->rotmode > 0) {
        copy_v3_v3(oldEul, pchan->eul);
        rot_order = pchan->rotmode;
        use_eulers = true;
      }
  
      if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
        if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
          /* Just like how the constraints do it! */
          copy_m4_m4(mat, pchan->pose_mat);
          BKE_constraint_mat_convertspace(
              ob, pchan, NULL, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
        }
        else {
          /* Specially calculate local matrix, since chan_mat is not valid
           * since it stores delta transform of pose_mat so that deforms work
           * so it cannot be used here for "transform" space. */
          BKE_pchan_to_mat4(pchan, mat);
        }
      }
      else {
        /* World-space matrix. */
        mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat);
      }
    }
    else {
      /* Object. */
      if (ob->rotmode > 0) {
        copy_v3_v3(oldEul, ob->rot);
        rot_order = ob->rotmode;
        use_eulers = true;
      }
  
      if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
        if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
          /* Just like how the constraints do it! */
          copy_m4_m4(mat, ob->obmat);
          BKE_constraint_mat_convertspace(
              ob, NULL, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
        }
        else {
          /* Transforms to matrix. */
          BKE_object_to_mat4(ob, mat);
        }
      }
      else {
        /* World-space matrix - just the good-old one. */
        copy_m4_m4(mat, ob->obmat);
      }
    }
  
    /* Check which transform. */
    if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) {
      /* Not valid channel. */
      return 0.0f;
    }
    if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) {
      /* Cubic root of the change in volume, equal to the geometric mean
       * of scale over all three axes unless the matrix includes shear. */
      return cbrtf(mat4_to_volume_scale(mat));
    }
    if (ELEM(dtar->transChan, DTAR_TRANSCHAN_SCALEX, DTAR_TRANSCHAN_SCALEY, DTAR_TRANSCHAN_SCALEZ)) {
      /* Extract scale, and choose the right axis,
       * inline 'mat4_to_size'. */
      return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]);
    }
    if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) {
      /* Extract rotation as eulers (if needed)
       * - definitely if rotation order isn't eulers already
       * - if eulers, then we have 2 options:
       *     a) decompose transform matrix as required, then try to make eulers from
       *        there compatible with original values
       *     b) [NOT USED] directly use the original values (no decomposition)
       *         - only an option for "transform space", if quality is really bad with a)
       */
      float quat[4];
      int channel;
  
      if (dtar->transChan == DTAR_TRANSCHAN_ROTW) {
        channel = 0;
      }
      else {
        channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX;
        BLI_assert(channel < 4);
      }
  
      BKE_driver_target_matrix_to_rot_channels(
          mat, rot_order, dtar->rotation_mode, channel, false, quat);
  
      if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) {
        compatible_eul(quat + 1, oldEul);
      }
  
      return quat[channel];
    }
  
    /* Extract location and choose right axis. */
    return mat[3][dtar->transChan];
  }
  
  /* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
  static void quaternion_to_angles(float quat[4], int channel)
  {
    if (channel < 0) {
      quat[0] = 2.0f * saacosf(quat[0]);
  
      for (int i = 1; i < 4; i++) {
        quat[i] = 2.0f * saasinf(quat[i]);
      }
    }
    else if (channel == 0) {
      quat[0] = 2.0f * saacosf(quat[0]);
    }
    else {
      quat[channel] = 2.0f * saasinf(quat[channel]);
    }
  }
  
  /* Compute channel values for a rotational Transform Channel driver variable. */
  void BKE_driver_target_matrix_to_rot_channels(
      float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4])
  {
    float *const quat = r_buf;
    float *const eul = r_buf + 1;
  
    zero_v4(r_buf);
  
    if (rotation_mode == DTAR_ROTMODE_AUTO) {
      mat4_to_eulO(eul, auto_order, mat);
    }
    else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) {
      mat4_to_eulO(eul, rotation_mode, mat);
    }
    else if (rotation_mode == DTAR_ROTMODE_QUATERNION) {
      mat4_to_quat(quat, mat);
  
      /* For Transformation constraint convenience, convert to pseudo-angles. */
      if (angles) {
        quaternion_to_angles(quat, channel);
      }
    }
    else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X &&
             rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) {
      int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X;
      float raw_quat[4], twist;
  
      mat4_to_quat(raw_quat, mat);
  
      if (channel == axis + 1) {
        /* If only the twist angle is needed, skip computing swing. */
        twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL);
      }
      else {
        twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL);
  
        quaternion_to_angles(quat, channel);
      }
  
      quat[axis + 1] = twist;
    }
    else {
      BLI_assert(false);
    }
  }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Variable Type Info
   * \{ */
  
  /* Table of Driver Variable Type Info Data */
  static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
      BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* Eval callback. */
      1,                                                              /* Number of targets used. */
      {"Property"},                                                   /* UI names for targets */
      {0}                                                             /* Flags. */
      END_DVAR_TYPEDEF,
  
      BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* Eval callback. */
      2,                                                        /* Number of targets used. */
      {"Object/Bone 1", "Object/Bone 2"},                       /* UI names for targets */
      {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
       DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
      END_DVAR_TYPEDEF,
  
      BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* Eval callback. */
      2,                                                        /* Number of targets used. */
      {"Object/Bone 1", "Object/Bone 2"},                       /* UI names for targets */
      {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
       DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
      END_DVAR_TYPEDEF,
  
      BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* Eval callback. */
      1,                                                                /* Number of targets used. */
      {"Object/Bone"},                                                  /* UI names for targets */
      {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY}                     /* Flags. */
      END_DVAR_TYPEDEF,
  };
  
  /* Get driver variable typeinfo */
  static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
  {
    /* Check if valid type. */
    if ((type >= 0) && (type < MAX_DVAR_TYPES)) {
      return &dvar_types[type];
    }
  
    return NULL;
  }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver API
   * \{ */
  
  /* Perform actual freeing driver variable and remove it from the given list */
  void driver_free_variable(ListBase *variables, DriverVar *dvar)
  {
    /* Sanity checks. */
    if (dvar == NULL) {
      return;
    }
  
    /* Free target vars:
     * - need to go over all of them, not just up to the ones that are used
     *   currently, since there may be some lingering RNA paths from
     *   previous users needing freeing
     */
    DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
      /* Free RNA path if applicable. */
      if (dtar->rna_path) {
        MEM_freeN(dtar->rna_path);
      }
    }
    DRIVER_TARGETS_LOOPER_END;
  
    /* Remove the variable from the driver. */
    BLI_freelinkN(variables, dvar);
  }
  
  /* Free the driver variable and do extra updates */
  void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar)
  {
    /* Remove and free the driver variable. */
    driver_free_variable(&driver->variables, dvar);
  
    /* Since driver variables are cached, the expression needs re-compiling too. */
    BKE_driver_invalidate_expression(driver, false, true);
  }
  
  /* Copy driver variables from src_vars list to dst_vars list */
  void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars)
  {
    BLI_assert(BLI_listbase_is_empty(dst_vars));
    BLI_duplicatelist(dst_vars, src_vars);
  
    LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) {
      /* Need to go over all targets so that we don't leave any dangling paths. */
      DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
        /* Make a copy of target's rna path if available. */
        if (dtar->rna_path) {
          dtar->rna_path = MEM_dupallocN(dtar->rna_path);
        }
      }
      DRIVER_TARGETS_LOOPER_END;
    }
  }
  
  /* Change the type of driver variable */
  void driver_change_variable_type(DriverVar *dvar, int type)
  {
    const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type);
  
    /* Sanity check. */
    if (ELEM(NULL, dvar, dvti)) {
      return;
    }
  
    /* Set the new settings. */
    dvar->type = type;
    dvar->num_targets = dvti->num_targets;
  
    /* Make changes to the targets based on the defines for these types.
     * NOTE: only need to make sure the ones we're using here are valid. */
    DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
      short flags = dvti->target_flags[tarIndex];
  
      /* Store the flags. */
      dtar->flag = flags;
  
      /* Object ID types only, or idtype not yet initialized. */
      if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
        dtar->idtype = ID_OB;
      }
    }
    DRIVER_TARGETS_LOOPER_END;
  }
  
  /* Validate driver name (after being renamed) */
  void driver_variable_name_validate(DriverVar *dvar)
  {
    /* Special character blacklist */
    const char special_char_blacklist[] = {
        '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-',  '/',  '\\',
        '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r',
    };
  
    /* Sanity checks. */
    if (dvar == NULL) {
      return;
    }
  
    /* Clear all invalid-name flags. */
    dvar->flag &= ~DVAR_ALL_INVALID_FLAGS;
  
    /* 0) Zero-length identifiers are not allowed */
    if (dvar->name[0] == '\0') {
      dvar->flag |= DVAR_FLAG_INVALID_EMPTY;
    }
  
    /* 1) Must start with a letter */
    /* XXX: We assume that valid unicode letters in other languages are ok too,
     * hence the blacklisting. */
    if (IN_RANGE_INCL(dvar->name[0], '0', '9')) {
      dvar->flag |= DVAR_FLAG_INVALID_START_NUM;
    }
    else if (dvar->name[0] == '_') {
      /* NOTE: We don't allow names to start with underscores
       * (i.e. it helps when ruling out security risks) */
      dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
    }
  
    /* 2) Must not contain invalid stuff in the middle of the string */
    if (strchr(dvar->name, ' ')) {
      dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE;
    }
    if (strchr(dvar->name, '.')) {
      dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT;
    }
  
    /* 3) Check for special characters - Either at start, or in the middle */
    for (int i = 0; i < sizeof(special_char_blacklist); i++) {
      char *match = strchr(dvar->name, special_char_blacklist[i]);
  
      if (match == dvar->name) {
        dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
      }
      else if (match != NULL) {
        dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL;
      }
    }
  
    /* 4) Check if the name is a reserved keyword
     * NOTE: These won't confuse Python, but it will be impossible to use the variable
     *       in an expression without Python misinterpreting what these are for
     */
  #ifdef WITH_PYTHON
    if (BPY_string_is_keyword(dvar->name)) {
      dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD;
    }
  #endif
  
    /* If any these conditions match, the name is invalid */
    if (dvar->flag & DVAR_ALL_INVALID_FLAGS) {
      dvar->flag |= DVAR_FLAG_INVALID_NAME;
    }
  }
  
  /* Add a new driver variable */
  DriverVar *driver_add_new_variable(ChannelDriver *driver)
  {
    DriverVar *dvar;
  
    /* Sanity checks. */
    if (driver == NULL) {
      return NULL;
    }
  
    /* Make a new variable. */
    dvar = MEM_callocN(sizeof(DriverVar), "DriverVar");
    BLI_addtail(&driver->variables, dvar);
  
    /* Give the variable a 'unique' name. */
    strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
    BLI_uniquename(&driver->variables,
                   dvar,
                   CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"),
                   '_',
                   offsetof(DriverVar, name),
                   sizeof(dvar->name));
  
    /* Set the default type to 'single prop'. */
    driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP);
  
    /* Since driver variables are cached, the expression needs re-compiling too. */
    BKE_driver_invalidate_expression(driver, false, true);
  
    /* Return the target. */
    return dvar;
  }
  
  /* This frees the driver itself */
  void fcurve_free_driver(FCurve *fcu)
  {
    ChannelDriver *driver;
    DriverVar *dvar, *dvarn;
  
    /* Sanity checks. */
    if (ELEM(NULL, fcu, fcu->driver)) {
      return;
    }
    driver = fcu->driver;
  
    /* Free driver targets. */
    for (dvar = driver->variables.first; dvar; dvar = dvarn) {
      dvarn = dvar->next;
      driver_free_variable_ex(driver, dvar);
    }
  
  #ifdef WITH_PYTHON
    /* Free compiled driver expression. */
    if (driver->expr_comp) {
      BPY_DECREF(driver->expr_comp);
    }
  #endif
  
    BLI_expr_pylike_free(driver->expr_simple);
  
    /* Free driver itself, then set F-Curve's point to this to NULL
     * (as the curve may still be used). */
    MEM_freeN(driver);
    fcu->driver = NULL;
  }
  
  /* This makes a copy of the given driver */
  ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver)
  {
    ChannelDriver *ndriver;
  
    /* Sanity checks. */
    if (driver == NULL) {
      return NULL;
    }
  
    /* Copy all data. */
    ndriver = MEM_dupallocN(driver);
    ndriver->expr_comp = NULL;
    ndriver->expr_simple = NULL;
  
    /* Copy variables. */
  
    /* To get rid of refs to non-copied data (that's still used on original). */
    BLI_listbase_clear(&ndriver->variables);
    driver_variables_copy(&ndriver->variables, &driver->variables);
  
    /* Return the new driver. */
    return ndriver;
  }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Expression Evaluation
   * \{ */
  
  /* Index constants for the expression parameter array. */
  enum {
    /* Index of the 'frame' variable. */
    VAR_INDEX_FRAME = 0,
    /* Index of the first user-defined driver variable. */
    VAR_INDEX_CUSTOM
  };
  
  static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver)
  {
    /* Prepare parameter names. */
    int names_len = BLI_listbase_count(&driver->variables);
    const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM);
    int i = VAR_INDEX_CUSTOM;
  
    names[VAR_INDEX_FRAME] = "frame";
  
    LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
      names[i++] = dvar->name;
    }
  
    return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM);
  }
  
  static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
  {
    /* Check if the 'frame' parameter is actually used. */
    return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME);
  }
  
  static bool driver_evaluate_simple_expr(ChannelDriver *driver,
                                          ExprPyLike_Parsed *expr,
                                          float *result,
                                          float time)
  {
    /* Prepare parameter values. */
    int vars_len = BLI_listbase_count(&driver->variables);
    double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM);
    int i = VAR_INDEX_CUSTOM;
  
    vars[VAR_INDEX_FRAME] = time;
  
    LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
      vars[i++] = driver_get_variable_value(driver, dvar);
    }
  
    /* Evaluate expression. */
    double result_val;
    eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(
        expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val);
    const char *message;
  
    switch (status) {
      case EXPR_PYLIKE_SUCCESS:
        if (isfinite(result_val)) {
          *result = (float)result_val;
        }
        return true;
  
      case EXPR_PYLIKE_DIV_BY_ZERO:
      case EXPR_PYLIKE_MATH_ERROR:
        message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error";
        CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression);
  
        driver->flag |= DRIVER_FLAG_INVALID;
        return true;
  
      default:
        /* Arriving here means a bug, not user error. */
        CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression);
        return false;
    }
  }
  
  /* Compile and cache the driver expression if necessary, with thread safety. */
  static bool driver_compile_simple_expr(ChannelDriver *driver)
  {
    if (driver->expr_simple != NULL) {
      return true;
    }
  
    if (driver->type != DRIVER_TYPE_PYTHON) {
      return false;
    }
  
    /* It's safe to parse in multiple threads; at worst it'll
     * waste some effort, but in return avoids mutex contention. */
    ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver);
  
    /* Store the result if the field is still NULL, or discard
     * it if another thread got here first. */
    if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) {
      BLI_expr_pylike_free(expr);
    }
  
    return true;
  }
  
  /* Try using the simple expression evaluator to compute the result of the driver.
   * On success, stores the result and returns true; on failure result is set to 0. */
  static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
                                              ChannelDriver *driver_orig,
                                              float *result,
                                              float time)
  {
    *result = 0.0f;
  
    return driver_compile_simple_expr(driver_orig) &&
           BLI_expr_pylike_is_valid(driver_orig->expr_simple) &&
           driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time);
  }
  
  /* Check if the expression in the driver conforms to the simple subset. */
  bool BKE_driver_has_simple_expression(ChannelDriver *driver)
  {
    return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple);
  }
  
  /* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
   * time dependencies nor special exceptions in the depsgraph evaluation. */
  static bool python_driver_exression_depends_on_time(const char *expression)
  {
    if (expression[0] == '\0') {
      /* Empty expression depends on nothing. */
      return false;
    }
    if (strchr(expression, '(') != NULL) {
      /* Function calls are considered dependent on a time. */
      return true;
    }
    if (strstr(expression, "frame") != NULL) {
      /* Variable `frame` depends on time. */
      /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */
      return true;
    }
    /* Possible indirect time relation s should be handled via variable targets. */
    return false;
  }
  
  /* Check if the expression in the driver may depend on the current frame. */
  bool BKE_driver_expression_depends_on_time(ChannelDriver *driver)
  {
    if (driver->type != DRIVER_TYPE_PYTHON) {
      return false;
    }
  
    if (BKE_driver_has_simple_expression(driver)) {
      /* Simple expressions can be checked exactly. */
      return driver_check_simple_expr_depends_on_time(driver->expr_simple);
    }
  
    /* Otherwise, heuristically scan the expression string for certain patterns. */
    return python_driver_exression_depends_on_time(driver->expression);
  }
  
  /* Reset cached compiled expression data */
  void BKE_driver_invalidate_expression(ChannelDriver *driver,
                                        bool expr_changed,
                                        bool varname_changed)
  {
    if (expr_changed || varname_changed) {
      BLI_expr_pylike_free(driver->expr_simple);
      driver->expr_simple = NULL;
    }
  
  #ifdef WITH_PYTHON
    if (expr_changed) {
      driver->flag |= DRIVER_FLAG_RECOMPILE;
    }
  
    if (varname_changed) {
      driver->flag |= DRIVER_FLAG_RENAMEVAR;
    }
  #endif
  }
  
  /** \} */
  
  /* -------------------------------------------------------------------- */
  /** \name Driver Evaluation
   * \{ */
  
  /* Evaluate a Driver Variable to get a value that contributes to the final */
  float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
  {
    const DriverVarTypeInfo *dvti;
  
    /* Sanity check. */
    if (ELEM(NULL, driver, dvar)) {
      return 0.0f;
    }
  
    /* Call the relevant callbacks to get the variable value
     * using the variable type info, storing the obtained value
     * in `dvar->curval` so that drivers can be debugged. */
    dvti = get_dvar_typeinfo(dvar->type);
  
    if (dvti && dvti->get_value) {
      dvar->curval = dvti->get_value(driver, dvar);
    }
    else {
      dvar->curval = 0.0f;
    }
  
    return dvar->curval;
  }
  
  static void evaluate_driver_sum(ChannelDriver *driver)
  {
    DriverVar *dvar;
  
    /* Check how many variables there are first (i.e. just one?). */
    if (BLI_listbase_is_single(&driver->variables)) {
      /* Just one target, so just use that. */
      dvar = driver->variables.first;
      driver->curval = driver_get_variable_value(driver, dvar);
      return;
    }
  
    /* More than one target, so average the values of the targets. */
    float value = 0.0f;
    int tot = 0;
  
    /* Loop through targets, adding (hopefully we don't get any overflow!). */
    for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
      value += driver_get_variable_value(driver, dvar);
      tot++;
    }
  
    /* Perform operations on the total if appropriate. */
    if (driver->type == DRIVER_TYPE_AVERAGE) {
      driver->curval = tot ? (value / (float)tot) : 0.0f;
    }
    else {
      driver->curval = value;
    }
  }
  
  static void evaluate_driver_min_max(ChannelDriver *driver)
  {
    DriverVar *dvar;
    float value = 0.0f;
  
    /* Loop through the variables, getting the values and comparing them to existing ones. */
    for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
      /* Get value. */
      float tmp_val = driver_get_variable_value(driver, dvar);
  
      /* Store this value if appropriate. */
      if (dvar->prev) {
        /* Check if greater/smaller than the baseline. */
        if (driver->type == DRIVER_TYPE_MAX) {
          /* Max? */
          if (tmp_val > value) {
            value = tmp_val;
          }
        }
        else {
          /* Min? */
          if (tmp_val < value) {
            value = tmp_val;
          }
        }
      }
      else {
        /* First item - make this the baseline for comparisons. */
        value = tmp_val;
      }
    }
  
    /* Store value in driver. */
    driver->curval = value;
  }
  
  static void evaluate_driver_python(PathResolvedRNA *anim_rna,
                                     ChannelDriver *driver,
                                     ChannelDriver *driver_orig,
                                     const AnimationEvalContext *anim_eval_context)
  {
    /* Check for empty or invalid expression. */
    if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
      driver->curval = 0.0f;
    }
    else if (!driver_try_evaluate_simple_expr(
                 driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) {
  #ifdef WITH_PYTHON
      /* This evaluates the expression using Python, and returns its result:
       * - on errors it reports, then returns 0.0f. */
      BLI_mutex_lock(&python_driver_lock);
  
      driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, anim_eval_context);
  
      BLI_mutex_unlock(&python_driver_lock);
  #else  /* WITH_PYTHON */
      UNUSED_VARS(anim_rna, anim_eval_context);
  #endif /* WITH_PYTHON */
    }
  }
  
  /**
   * Evaluate an Channel-Driver to get a 'time' value to use
   * instead of `anim_eval_context->eval_time`.
   *
   * - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated.
   * - Has to return a float value.
   * - \a driver_orig is where we cache Python expressions, in case of COW
   */
  float evaluate_driver(PathResolvedRNA *anim_rna,
                        ChannelDriver *driver,
                        ChannelDriver *driver_orig,
                        const AnimationEvalContext *anim_eval_context)
  {
    /* Check if driver can be evaluated. */
    if (driver_orig->flag & DRIVER_FLAG_INVALID) {
      return 0.0f;
    }
  
    switch (driver->type) {
      case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */
      case DRIVER_TYPE_SUM:     /* Sum values of driver targets. */
        evaluate_driver_sum(driver);
        break;
      case DRIVER_TYPE_MIN: /* Smallest value. */
      case DRIVER_TYPE_MAX: /* Largest value. */
        evaluate_driver_min_max(driver);
        break;
      case DRIVER_TYPE_PYTHON: /* Expression. */
        evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context);
        break;
      default:
        /* Special 'hack' - just use stored value
         * This is currently used as the mechanism which allows animated settings to be able
         * to be changed via the UI. */
        break;
    }
  
    /* Return value for driver. */
    return driver->curval;
  }
  