maprange function

Hi all,

I’m working on using pydrivers for rigging facial animation, and I think I need a function like so:

maprange(input, input min, input max, output min, output max)

For example, if I run it like so:

maprange(0.5, 0, 1, 0, 2)

then the return value would be 1, but if I run it like so:

maprange(0.5, 0, 1, 1, 0)

the return value would stay the same.

I know what I want the function to do, but I’m terrible at math and just can’t wrap my head around this. Any help would be greatly appreciated, and sorry if this has been brought up before, but I didn’t see it when I searched (could have used the wrong words) and I didn’t see it in Blender.Mathutils.

Try this:


def map_range (input, inpMin, inpMax, outMin, outMax):
   # clamp input to range
   if input <= inpMin:
      nInput = inpMin
   elif input >= inpMax:
      nInput = inpMax
   else:
      nInput = input

   # obtain diffs of ranges
   inpRange = inpMax - inpMin
   outRange = outMax - outMin
   
   # get offset of nInput from start of input range
   xInput = (nInput - inpMin) / float(inpRange)

   # obtain position
   return ((xInput * outRange) + outMin)


I’m not sure whether this is what it is supposed to do, but judging from your examples, this is quite close.

Warning: I haven’t tested this code, and it’s been a little while since I’ve done any python programming.
Warning 2: This code assumes that the max values are larger than the min values. If this is not the case, then expect errors

Aligorith

800th Post!

This version should work ok. Includes checking for odd ranges…


# map a value within one range onto another range
def map_range (input, inpMin, inpMax, outMin, outMax):
   # just a small helper function
   def SWAP (a, b):
      c = a
      a = b
      b = c
      return a,b
      
   # make sure that ranges are ok
   if inpMin > inpMax:
      inpMin, inpMax = SWAP(inpMin, inpMax)
   if outMin > outMax:
      outMin, outMax = SWAP(outMin, outMax)

   # clamp input to range
   if input <= inpMin:
      nInput = inpMin
   elif input >= inpMax:
      nInput = inpMax
   else:
      nInput = input

   # obtain diffs of ranges
   inpRange = inpMax - inpMin
   outRange = outMax - outMin
   
   # get offset of nInput from start of input range
   xInput = (nInput - inpMin) / float(inpRange)

   # obtain position
   return ((xInput * outRange) + outMin)

Aligorith

Heya,

Thanks for the quick response. Unfortunately, I’ve tried the above out twice and I get very strange results. For positive values it seems to work perfectly, such as:

map_range(0.5, 0, 1, 0, 2) --> 1.0
map_range(0.5, 0, 1, 1, 2) --> 1.5

Those seem normal. However, I’m curious about some other results:

map_range(0.3, 0, 1, -1, 0) --> -0.7?

That means that the value isn’t so much getting remapped as shifted backwards… or am I misunderstanding the output?

Thanks again in advance.

edit: you posted a second after I posted the above, trying the new code now…
edit 2: Same problem as above with 0.3.

Ahh. I see why it’s not reversing the negative values… I might try fixing it myself in the morning.

What were you expecting from
map_range(0.3, 0, 1, -1, 0) ?
… -0.3?

Aligorith

Exactly.

Lemme expand on why I need this function. I’m working with the book Lightwave 3D Cartoon Character Creation, and in it are a number of functions that seem to be lightwave builtins which he uses for setting up joystick controls. map_range is one of them, and I thought that one of its uses was to remap positive to negative by flipping over the axis. Now that I’ve had the night to think about it I’m thinking I’m wrong and that you’ve given me exactly what I need. So ignore the above post of a sleepy man. Thanks a lot for the function!

Side note, pydrivers only seem to work for driving shape keys if you use an object. I tried something like this:

shape key influence = ArmatureObject.getPose().bones[“Driver.EyesLeft”].loc[2]

… and it doesn’t update in real time. Is this a known thing or have I done something wrong? It works fine if I do

shape key influence = Object.Get(“DriverCubeOb”).LocZ

Thanks again!

Works for me. Make sure you’re using the correct axis :slight_smile: (Select the bone and use “Normal” transform orientation).

Mike

Lol… you know, it’s workng now and I think that was the reason. ^_^;

Thanks to you both again!

I’m curious about this. Can anyone elaborate? What is the process?

I just meant to use the “Normal transform” option in the “Widget” to manually move the bones around or just observe the axis arrows, to be able to predict what the ArmatureObject.getPose().bones[“Driver.EyesLeft”].loc[2] code would do. AFAIK the .loc[1/2/3] transforms correspond with the “Normal” interactive transforms.

If there’s a way to use Global/Local transforms in a script for Pose bones, I don’t know about it.

Mike

I think you can get the global transform by a bone by multiplying the bone’s pose matrix by that of its parents and the armature object, although don’t quote me on that.

But Basil, here’s what I’ve been working on.

  1. I have an object with shape keys.
  2. With the object selected, I go into the IPO editor and choose shapes in the dropdown.
  3. I select the shape I want to drive, hit N, hit Add Driver, then press the snake to change it into a python driver.
  4. I type in some value to make it attach to the movement of an object. If it’s a normal object, maybe something like
ob("DriverObject").LocZ

would attach the influence of that key to the object’s Z location. I actually have a pydrivers.py file which has a clamp function, so my code would be something closer to, say

p.clamp(ob("DriverObject").LocZ, 0, 1)

which would limit the value between 0 and 1 should the Z location of the object be greater or lesser. The code for accessing posed bone positions is above.

I hope that helps.

Thank you so much, works perfectly!!!
I needed this for a c++ program I was writiing and since I converted it for c++ the least I could do is post it here for anyone that may come across it. Easiest port ever btw :slight_smile:
float map_range(float input, float inpMin, float inpMax, float outMin, float outMax)
{
float nInput = 0.0f;
// clamp input to range
if(input <= inpMin)
nInput = inpMin;
else if(input >= inpMax)
nInput = inpMax;
else
nInput = input;
// obtain diffs of ranges
float inpRange = inpMax - inpMin;
float outRange = outMax - outMin;

// get offset of nInput from start of input range
float xInput = (nInput - inpMin) / float(inpRange);
//obtain position
return ((xInput * outRange) + outMin);
}

Funny thing: I was looking at this post earlier today at work because I needed it for a rigging problem, but then I found out Maya has a node that does the same thing for you. ^_^;

Thanks for adding the C code; I’m sure someone’ll find it useful some day.

this is such a cool thread, it would be great if someone using this script could post images/mov’s of the pydrivers using this method in action.