Adobe Director: Type overloading using ancestors




Warning! This is a heavily geeky Adobe Director post! This is something I've messed around with in Director for a while now, and I recently brought it up on Direct-L. A lot of people there hadn't heard about it so I thought I'd blog about it here.

An object's ancestor property allows you to attach a secondary object that will get events that the original object doesn't get. For example you can attach a "car" child object as an ancestor to a "Probe" child object. Once you've done that, any events sent to "Probe" that the probe script doesn't process will be passed on to "car". If you're confused, look it up in the help docs, because it's only going to get , um, confusinger.

I discovered a while back that you can assign other types to a child object's ancestor property and have the resulting object act like the ancestor's ilk. And by "ilk" I mean lists, property lists, rect(), point(), image objects ... you name it and it probably works.

Here's an example. Let's say I wanted a list that returned void whenever an out of bounds index was referenced. From a usage standpoint I'd want it to look like this:
ilist = [1, 2, 3]
Put ilist[4]
-- void
put ilist[0]
-- void
So that's how we want it to work. But currently if you try this with Director, you'll get errors if you try to access invalid pointers. However, using this trick we can build an overloaded list that will work the way we want to. Here's some code that goes in a parent script:

-- Parent Script: "BetterList"

property ancestor

on new me
  ancestor = []
  return me
end

on getat me, iposition
  if iposition <1 or iposition > ancestor.count then return void
  return ancestor[iposition] 
end

Notice how all this really does is assign an empty list to the ancestor, and a getat function that checks to see if the requested index is out of bounds.

Now, if we do this:
ilist = new(script "betterlist")
ilist[1] = "hello"
ilist[2] = "there"

put ilist[1]
-- "hello"

put ilist[3]
-- Void

The ilist variable operates as a normal list: we can address it with brackets, and we can add items to the list like normal. However, when calling an out of bounds index, the list returns void. This is because the GetAt function (which bracket syntax is shorthand for) is being processed not by the list, but by my script. However, all other functions are unused by the child object and are therefore passed to the ancestor.

On Direct-L Valentin Schmidt took my script and modified it to create a way to make a list that starts at 0 instead of one. This could be very useful when porting code from other languages where arrays start with zero:
--- parent script "zero_list"
property ancestor

on new me
 l = []
 repeat with i = 2 to the paramcount
   l.append(param(i))
 end repeat
 ancestor = l
 return me
end

on getAt me, pos
 if pos < 0 or iposition >= ancestor.count then return void
 return ancestor[pos+1]
end

on setAt me, pos, val
 ancestor[pos+1] = val
end

--- test
l = script("zero_list").new(1,2,3)
put l[0]
-- 1
l[0] = 23
put l[0]
-- 23


How useful is this hidden functionality? Only time will tell. It has worked with every data type I've tried. Drop me a comment if you've used this before or have any insight as to what can be done with it.





Feedback - 5 responses

Displayed newest to oldest. Leave a comment.
Hanford wrote:   
Test.
Phylobates wrote:   
I'm been using director for years and you think you got it pretty much sussed when a little gem of knowledge like that pops up.. interesting. Tried with "rect" and it works beautifully. I'm sure I'll find some use for it...


-- New rect object in script customRect
on new me
me.ancestor = rect(0,0,0,0)
return me
end

on diagonalLength me
return sqrt((me.width*me.width) + (me.height * me.height))
end

-- and lo and behold..

myrect = new script("customRect")
myrect.top = 50
myrect.right = 50
put myrect.diagonalLength()
-- 71
ahoeben wrote:   
The 'on put me' approach does not seem to work. Your reply got me thinking though, and the solution is as simple as:

myList = new(script(“BetterList”),[11,22,33])

put myList.ancestor
-- [11,22,33]

put myList.ancestor * 4
-- [44,88,132]

put myList.ancestor + [1,2,3]
-- [12,24,36]
Hanford wrote:   
Interesting about losing those aspects of list functionality. You can, of course write your own "put", like this:

on put me
trace(ancestor)
end

I like the change you made to the "new" handler. Valentin does something similar, but in my original example I wanted it to be as simple as possible.
ahoeben wrote:   
If you change the 'new' handler slightly, you can set initial list values upon script initialisation:


on new me, aList
if not listP(aList) then aList = []
ancestor = aList

return me
end

usage:
myList = new(script("BetterList"),[11,22,33])
put myList[2]
-- 22

While this technique is very usefull to add 'methods' to the list datatype (such as an 'addOnce' method to only add an item to a list if it is not already in the list, or an 'append' to append another list to this list, etc), you also loose a lot of list functionality:
- you can no longer 'put' the list
- you can no longer do arithmetics on the full list (eg multiply all list items by 2)
I think that's the reason why acestors aren't used more to extend datatypes...

Leave a comment

Name:
Website:
Comment:

Email:
Captcha:
Identify the image of the Sun:

           

           

 

Video Game Design

User Interface Design

Creative & fun stuff

 

I'm Hanford Lemoore. My parking skills are unparalleled.

I design things in Silicon Valley; mostly consumer electronics media players. Perhaps I can design things for you. Check out my UI Portfolio.

When I'm not making things for other people, I'm usually making video games. more

 

Please use our contact form.

 

RSS 2.0

 

monolux.com

tikiroom.com

junkyardclubhouse.com

 


 

   


Copyright 2010 Hanford Lemoore | Blog | About | Portfolio | Contact
Powered by Olark