Notifications
Clear all

[Closed] What am I doing wrong with a for loop?

This is driving me crazy. I am trying to use a for loop in a function to use intersectRay on an array called ground. This code works but only on ground[1] so the first item in the listbox. So I know it works bit I believe the for loop is not working or is always giving a value of 1 but i cant figure out why.

fn getIntersection theRay= ( if ground[1]!=undefined and (isValidNode ground[1].node) then ( for i in ground do i ( intersectRay ground[i].node rayV ) ) else ( undefined ) )
8 Replies

The problem is here:

for i in ground do i ( intersectRay ground[i].node rayV )

You probably need to change it to:

for i in 1 to ground.count do ( intersectRay ground[i].node rayV )

-Eric

Ah, thank you so much. The for loop works great now. The problem now is that I am getting a value of Ok to the script controller when I should be getting a position value. So the error is “no “pos” value for Ok”. To me it looks like the intersect ray is returning ok as a value because it is in the for loop.

position controller script:



--Variables
master= attribute holder containing the intersect ray script and for loop
masterObj = Object that contains the attribute holder
rayObj=Ray casting object
--Script

theRay=(ray rayObj.pos (-rayObj.transform.row3)) 
hit=master.getIntersection theRay 
if hit==undefined then 
(
masterObj.pos
) 
else 
(
hit.pos
)


--intersectRay script in the attribute holder
	fn getIntersection theRay =
	(
		if ground[1]!=undefined and (isValidNode ground[1].node) then
			(
				for i in 1 to ground.count do
					(
							
						intersectRay ground[i].node theRay
					)
			)
			else 
				( 
					undefined 
				)

	)		

Correct. The OK value you get is the result of the for loop itself.
If you want a returned value from that loop, you’ll want to either…
A. declare a variable and assign to that variable inside the loop


myVar = someVar
for o in objs do myVar = someValue)
myVar

or…
B. call return when you’ve hit the value you need (note that ‘return’ is slow; see the help file)


for o in objs do (
  if (someCondition) do ( return someValue )
)

In your specific case, I’m not sure what value you would be hoping to return from the function as you are shooting multiple rays? Would you want the first hit, the last hit, an array of hits, the average? etc.

yea it took me a while but I finally got it figured out thanks to a tutorial on paul neales site. I have this thing working with multiple objects pretty well. The one thing I am still missing is that when the ray dosent interesct anything I want the object to return to the position of a master object. Try as I might I cant get that part to work. any help on this would be greatly appreciated. I am incuding the max file just so everyone can get a better idea of what I am trying to do.

position controller script…


rayV=(ray rayObj.pos (-rayObj.transform.row3))
 for x in master.ground do
(
 
	 local myAr = master.myFn x.node rayV
  format "myAr[1]: %
 myAr[2]: %
" myAr[1] myAr[2]
  if myAr[1] != undefined do
   (   
	format "myAr[1].pos: %
 myAr[2] object: %
" myAr[1].pos myAr[2]
	global hit=myAr[1]
	hit.pos
   )
 
)
--if hit == undefined then
--(masterObj.pos)
--else
(hit.pos)

Attribute holder…


ca = custAttributes.getDef $.modifiers[1].motorgradermaster
attributes motorgradermaster
redefine:ca
(
 parameters main rollout:motorgrader
 (
  
  ground type:#maxObjectTab tabSize:0 tabSizeVariable:true
  
 )
 --#########################################
 --Functions
 --#########################################
 fn myFn myobj rayV =
  (
   local myAr = #()
   append myAr (intersectRay myobj rayV)
   append myAr myobj.name
   myAr 
  )

 --#########################################
 --Rollouts
 --#########################################
 rollout motorgrader "Motor Grader Rig" width:160 height:1000
 (

  local btW1=140
  local  btW2=65

  group "ground"
  (
  listBox listObj "Ground Objects:" height:5
  pickButton addObject "Add Object" width:btW2 across:2
  button deleteObject "Delete" width:btW2
  )

  fn updateList=
  (
   nNames=for n in ground collect n.node.name
   listObj.items=nNames
   
  )

  on addObject picked obj do
  (
   if classOf obj == Editable_mesh then
   (
   weak = (nodeTransformMonitor node:obj forwardTransformChangeMsgs:false)
   appendIfUnique ground weak
   updateList()
   )
  )
  on deleteObject pressed do
  (
   num=listObj.selection
   if num!=0 then
   (
	deleteItem ground num
	if listObj.items.count == 0 then ground=#()
	updateList()
   )
  )

  on motorgrader open do
  (
   updateList()
  )
 )
)

  

I am pretty sure the problem is the fact that I am always getting undefined because it is not always over all the objects. So i need to some how say that if all values in the array are undefined then go to masterObj.pos. But when I do that the intersect ray only works on the last object in the list box because there is an undefined value coming after all the other objects. Any ideas on how I can get around this?

I’m not sure why you would have an ‘undefined’ in your $.ground list; you’re only adding the ground objects?

One thing that does seem wrong is the ‘global hit’, though. That means that once hit -does- get assigned a value, any subsequent run of that position controller will no longer see it as ‘undefined’.
You should either set it undefined explicitly again before the loop – at which point you might as well use a ‘local hit’ variable instead.
e.g.


local hit = undefined
for x in master.ground do (
  if (condition) then ( hit = someValue )
)
if (hit == undefined) then ( -- no hits found
  -- move to default position
)
else ( -- 1 or more hits found, last-hit object stored
  -- move to hit position
)

Ah man your a genius. Thanks again man this is the second time you have helped me sort things out. I owe you a drink or something.

I was about to use the continue or exit to exit when it found the hit and store that value in the hit variable. but I am not sure that would have solved the problem. I though that might make it run faster if there where a large amount of objects in the list box.

continue/exit (or, within a function, return) -can- be faster… but looks like you’d need to have a very long or intensive loop to see benefits.

I do still use it in non-time-critical loops/etc., though. It makes the code a bit clearer to read… i.e.


fn someFunction = (
  if (someCondition) then ( return someValue )
  else (
	-- big lump of code here
  )
  5
)

versus


fn someFunction = (
  if (someCondition) then ( someValue )
  else (
	-- big lump of code here
  )
  5
)

In the first, you already know that it’s going to return someValue if a condition is met. In the second, you have to scroll down to make sure that there isn’t something outside of that if-then-else branching that would get returned.
code-collapsing in the new editor makes this a bit less of a pain, thankfully :>

Of course you could do…


fn someFunction = (
  local returnValue
   if (someCondition) then ( returnValue = someValue )
   else (
 	-- big lump of code here
   )
   returnValue
 )
 

which at least implies that ‘someValue’ should be the return value at the end of the function… but it -is- just an implied thing.

But that’s all coding preference blabla