Notifications
Clear all

[Closed] Why does this throw an error?

Hi…This has been driving me crazy. I have a small function that places a script controller on a spline connected to a morph target -it works fine when I enter each line in the listener, but throws an ‘expression error’ if I run it a part of the larger script.

 
fn connectmorphscriptcontrC upln  controlspline = 
 
(
ctrlobjcont = controlspline.pos.controller
 outerspline = controlspline.parent
 out = outerspline.width.controller
--Place float controllers on the morph channels to be used in case there aren't any and locate the  first free slot for float script controller..............................................
 
if poly.modifiers[#Morpher][upln].controller!=undefined and classOf poly.modifiers[#Morpher][upln].controller != Float_List do poly.modifiers[#Morpher][upln].controller = Float_List()
  
		   rp =3
 poly.modifiers[#Morpher][upln][rp].controller = float_script()
 ctrlf = float_script();
 poly.modifiers[#Morpher][upln][rp].controller = ctrlf;
 ctrlf.addtarget outerspline.name  out
 ctrlf.addtarget  controlspline.name ctrlobjcont;
 print ctrlobjcont[1].value
  str =  "X = ctrlobjcont[1].value/out.value*200
Y = ctrlobjcont[2].value/out.value*200;
 percent =  ((if X >= 0 then 100 else X+100))

 scalarY =  ((if Y <0 then 0 else  Y/100 ))
 scalarY*percent "
  ctrlf.setexpression str
 
 )
  

It happens even when I put print statements that show each argument is being passed correctly , even up until the setexpression line, when it gives ‘No get function for undefined’ I don’t understand which expression is ‘undefined’…and, as I said , the script works fine when I go line by line. I have spent days trying to figure this out. I have made the relevant variables of global scope, and have re checked he code seperately as a standalone …
Please help.
Thank you for your time…

8 Replies

Since your code is impossible to use out of context (without the scene and knowing what else is needed), all I can say is that the expression is wrong.

When you run the code line by line, the variables that store the controllers become global implicitly. But these global variables would be gone next time you restart Max and load the scene.

Since you are adding the controllers as tracks in the float_script controller, you are supposed to actually use them. They will contain references to the selected controllers and tracks and will store them with the scene. You are naming them according to the object names they came from. So you are supposed to include in the expression the NAMES of the target tracks you just created, not the variable names that contain the controllers.

I guess the expression will look something like

str =  "X = "+outerspline.name+"[1].value/"+controlspline.name+".value*200
Y = "+outerspline.name+"[2].value/"+controlspline.name+".value*200;
 percent =  ((if X >= 0 then 100 else X+100))

 scalarY =  ((if Y <0 then 0 else  Y/100 ))
 scalarY*percent "

If your object names contain special characters or spaces, this might not work well either – you will have to make sure you replace any unsupported characters with _ first. But in the general case of Rectangle001 and Circle002 etc. it should be ok…

Thaank you sir, I will do as you say and report…

Sorry but I tried that, but the code could never work, as the expression contains no reference to the controller, so object.name[1] isn’t going to mean anything as its working on a string. Doesn’t the ‘add target’ take care of the naming bit?
I am still puzzled why the thing works line by line but not on the context of the rest of the script. Do you mean I have to name the controller itself? I’m sorry I’m still not getting it.

1 Reply
(@bobo)
Joined: 11 months ago

Posts: 0

How would you do it if you did NOT use MAXScript?
You would create two variables, one for each reference – out and ctrlobjcont – and then use the VARIABLE in the expression, which would bring over the value of the respective controller the variable points at.

Now you have to recreate the above using MAXScript. You do most of it, but then instead of referencing the names of the flat_script controller’s variables, you reference the creation script’s variable names “out” and “ctrlobjcont” which are potentially not defined in the scope of the controller’s expression. They are only defined if you evaluate the code line by line, because that puts implicitly every variable in global scope. But if you save the scene, close Max and reopen the scene, those globals would be gone and nothing would work.

I cannot run your code because I don’t have your scene and the rest of the script. But the above is all you need to know to solve the issue. Create variables within the float_script. Assign these variables the controllers you need. Use those variables within the expression, with the same names you gave them when you created them.

Here is a simplest example:

b = box pos:[-100,0,0]
s = sphere pos:[100,0,0]
fc = float_script()
s.radius.controller = fc
theCtrl = b.pos.controller
fc.addTarget b.name theCtrl
fc.setExpression ("length "+b.name)

This creates a Sphere and a Box.
It sets the Radius of the Sphere to a Float_Script.
In the Float_Script, a variable called the same as the box’s name is created and set to the Box’s position. In the expression, that same variable’s length is evaluated. Note I use the name of the box in both addTarget AND in the setExpression, since that’s the variable holding the reference to the box position controller, not theCtrl as you did in your code.

If you execute and then move the Box around, its distance to the world origin will define the radius of the Sphere.

You want the same, just with some more complex tracks of a Morpher.

Ok I think I get it…although isn’t ‘length’ a property of the box, not the sphere? Shouldn’t it be radius box? Why is length a variable? Is it the distance to the origin? Anyway Bobo thanks for your time. guess I will go through it all…

Just before you go…what would the expression be if i wanted the sphere radius to grow with the x distance only of the box? Thanks again for your time and patience

1 Reply
(@bobo)
Joined: 11 months ago

Posts: 0

To answer most of your questions:

length() is a function that returns the (surprise!) length of a vector. Since a Position is a vector, the length of the position is the distance to the world origin.

So the expression of the float_script controller assigned to the radius track of the Sphere says: get the position of the box, calculate its length and assign to the result to the radius.

To set the radius based on the X axis only, you would have to say

fc.setExpression ("abs "+b.name+".x")

The abs() takes care of the sign, so negative X and positive X both produce a valid result.

In other words, the whole example would be

b = box pos:[-100,0,0]
s = sphere pos:[100,0,0]
fc = float_script()
s.radius.controller = fc
theCtrl = b.pos.controller
fc.addTarget b.name theCtrl
fc.setExpression ("abs "+b.name+".x")

Thank you very much Bobo…I get it now…cheers I I guess b.name[1] would work as well?