Notifications
Clear all

[Closed] proper use of structures and what are they good for

I been trying to understand the proper use of structures and why people use them for.

This is a bit of information i;ve been serqching for:

I always reading in a lot fo places why the structures are good.Like in PaulHormis Site http://www.hyperent.com/ in maxscript.

Remi McGill www.blur.com
Thanks for teaching me about Structs and the reasoning behind using them. Ever since you told me what they are and how they work I have put EVERY script I write into a struct. It has really paid off in the consistency and reliability of my scripts.”

On Carlos Anguiano site i check the script LosCacheAndBake and he use the structure for everithing UI , functions … al is based in a structure.

http://www.losart3d.com/

On thread in cgtalk http://forums.cgsociety.org/showthread.php?f=98&t=473232 eek posted

“globals!! locals!!? i use structs for everthing”

F97ao writed in a thread too

http://forums.cgsociety.org/showpost.php?p=2594011&postcount=4

on http://forums.cgsociety.org/showthread.php?f=98&t=293899 HYPERLINK “ http://forums.cgsociety.org/showthread.php?f=98&t=293899”

Paul posted

I use structs a lot to keep my code organized and remove as many possible global variables as I can from the system. I used to get confliting function names all the time. For most of the code that I’m doing the time difference realy dooesn’t matter but when it does I will go with the faster solution.

and bobo write on area

” if your struct contains local variables, you cannot access them unless you
create an instance of the struct.
If the struct contains only functions delimited by commas, you can directly
call these functions without creating an instance.

So the only use Ive been able to do is use structure to contain function.


struct test_st 
 
(
 
fn test01 =
 
(print "a"),
 
 
 
fn test02 =
 
(print "b"),
 
 
 
fn test03 =
 
(print "c"),
 
 
 
endofstruct
 
)
 
structest = test_st()
 
structest.test01()
 
structest.test02()
 
structest.test03()
 

But was is the main advantage of it ??, you could have the functions alone and call them separated. like normal functions.Is any advantage of using atructure are quicker than functions , more stable or…

or is only to avoid confusion of names as paul said.

Normally i got variables than contain : states like integer , arrays ,or true or false and i force to create them as global to force my code to be stable. so probaly you can include this variables in the structure but how you update them and how you call them for outside of the structure

Can anyone explain the use of a structure as an instance and without being and instance.

and the way to do a structure Inheritance for other structures, that a friend of mine told me is the real power of programing objects.(structures)

14 Replies

Structs are really useful in larger scripting projects. On smaller scripts I’m not afraid to create a few locals, but let’s say for instance, you’re loading complex values into a listbox (values that maybe contain a node, several strings to fill the listbox, maybe some custom #name values, etc), you could just throw all of these values into a struct, and everything becomes much more organized. So now you have an array of structs all holding the information you need to organize your objects, and all easily accessible as properties, to boot.

So you can do cool stuff like this:


(
	struct layer (name, state, weight)
	
	newLayer1 = layer name:"layer1" state:false weight:100
	newLayer2 = layer name:"layer2" state:true weight:100
	
	print newLayer1.state
	print newLayer2.name
)

These are just fake properties that don’t mean anything, but it just shows how you can organize things much better when you can create struct “objects.” Its not a true object like in other languages, its just a composite value type that gets the job done almost as well. As far as struct inheritance, I’m not sure if you can do that in MXS. Class inheritance is common place in other languages, though.

The reason to have functions defined in a struct is for organization. A good example of this is the “filterfunctions.ms” file that installs with max. It avoids a lot of name conflicts, and provides a way to easily package oft-used functions under something similar to a namespace in other programming languages.

Maybe someone else can chime in on how they use structs in their scripts.

Hi!

Pretty much the same thing as d3coy said I use them to store massive amounts of face, vert and material data (including arrays of structs within other structures) read in from textfiles.

I’ve also packaged up a load of useful functions into a ‘handyTools’ structure which I can then use in scripts in the same way you might use a ‘polyOp.’ for example:


  objSel = for o in objects where matchPatten o.name pattern:"Object*" collect o
  
  handyTools.renameObjSequence objSel newName:"Wall_Fragment_"
  

Structures are handy for storing lots of data and are easier to use than multidimensional arrays, for example:


   struct fruitBowl ( name, numberOfApples, numberOfPears )
   
   fruitCollection = for f=1 to 5 collect (fruitbowl name:("Fruity"+f as string) numberOfApples:(random 1 10) numberOfPears:(random 1 10) )
   
   format "name: %
" fruitCollection[3].name
   format "numApples: %
" fruitCollection[3].numberOfApples
   format "numPears: %
" fruitCollection[3].numberOfPears
   
   fruitCollection[3].numberOfPears = 100
   
   fruitCollection[3].numberOfPears
   
   

They can also be used as a way of storing functions relating to a particular tool without having to worry about having lot’s of functions floating around with the same names:


   struct app1 
   ( 
   	fn printMe = ( print "This came from App1" )
   )
   
   struct app2 
   ( 
   	fn printMe = ( print "This came from App2" )
   )
   
   app1.printMe()
   app2.printMe()
   
 PEN

As well as storing data I use them all the time on larger scripts for managing all the code and packaging it nice and neat. Here is a quick example of what I do.

One of the advanatages of this is I can run all my scripts via command line and they don’t have to be open. I can set any of the parameters in them by setting variables that are in the struct and if I then open the UI it reflects those changes. I can then also call the functions in one script from another getting access to all the code stored in it.


  struct test 
  ( 
     --Variable to store an instance of the rollout so that we can access it  
  	  --easily from within struct. 
     theRolloutInstance=undefined, 
     --The calcualted value. 
     theVal=0.0, 
      
     --Function for calcualting the value. 
  	  --This store the calc'd value in theValue as well as sets the  
  	  --text in the UI label. Notice that it doesn't call on the Rollout 
  	  --directly but calls on the instance of the rollout that is local to the struct. 
     fn testFn val= 
     ( 
  	  --Set the value of the variable in the struct. 
  	  theVal=val*10.0 
  	   
  	  --Set the text to the label in the UI. 
  	  if theRolloutInstance!=undefined then 
  	  ( 
  		 theRolloutInstance.theValLb02.text=theVal as string 
  	  ) 
     ), 
      
     --Creates the rollout. 
     fn rolloutFn= 
     ( 
  	  --Destroy the dialog if theRolloutInstance isn't undefined.  
  	  if theRolloutInstance!=undefined then 
  	  ( 
  		 destroyDialog theRolloutInstance 
  		 theRolloutInstance=undefined 
  	  ) 
  	  --Create the rollout. 
  	  /* 
  		 The function below is in scope of the struct, however once the rollout 
  		 is created the rollout isn't in scope of the struct. The means that any 
  		 time the rollout needs to call into the struct is must use an instance of  
  		 the struct. 
  	  */ 
  	  rollout testR "Test" 
  	  ( 
  		 local btW1=140 
  		  
  		 label theValLb "The Value * 10 = " align:#right across:2 
  		 label theValLb02 " " align:#left 
  		  
  		 spinner valSp "Value:" scale:0.01 
  		 button printVal "Print Val" width:btW1 
  		  
  		 on valSp changed val do 
  		 ( 
 			--Run the testFn. You need to call the struct instance and then the function. 
  			testInst.testFn val 
  		 ) 
  		  
  		 on printVal pressed do 
  		 ( 
  			--Format theVal to the listener. You need to call the struct instance 
  			   --to get theValue. 
  			format "This is the Value: %
" (testInst.theVal) 
  		 ) 
  		  
  		 on testR open do 
  		 ( 
  			--While the rollout is being created theVal is in scope since the rollout 
 			   --hasn't been added to a dialog yet so there is no need to use the 
  			   --struct instance here.  
  			theValLb02.text=theVal as string 
  		 ) 
  	  ) 
  	  --Create the dialog. 
  	  createDialog testR 
  	  --Set the theRolloutInstance to be an instance of the rollout.  
  	  theRolloutInstance=testR 
     ) 
  ) 
  --Create an instance of the struct.  
  testInst=test() 
   
  /* test area 
  --Open the rollout. 
  testInst.rolloutFn() 
   
  --Set theValue manualy using the function.  
  testInst.testFn 200.52 
   
  --Get or set the value directly using this.  
  testInst.theValue 
   
  */ 
 
 eek

accesibilty on the fly, i.e as Paul mentioned i can change variables in the struct, fns, etc and see the change update easily. I kinda treat it as the css to an html doc.

 rdg

In addition and or duplication to what was said already:
I use them in a fake object oriented way.
I treat struct as objects that hold all necessary function for a specific task.
This way the ‘object’ is encapsulated and after intialising the struct I can be sure that I don’t encounter undefined variables/states. Like OOP this makes my code cleaner, stricter and less error-prone.

I don’t do complex setups like my previous posters, so my experience with inter-stuct-communication is limited. In reality my use of structs is everyting but object orientated where you would follow MFC or other patterns to seperate content,function and style … But maxscript is a scripting ‘environment’.

eek:
I like your css/html methaphor. As bathetic this whole web stuff is, in the last months I realize that some concepts are worth to be transcripted to other contexts like 3d (materials, rigging, animation etc).

Georg

One of the advantages I like of struct, is the way you can use the variables in loops.
Lets say you have a large amount of settings, which you’d like to save and load from a INI file. With a simple for loop you could do it like this:


  Struct settingsObject (
  	firstSetting, secondSetting, thirdSetting
  )
  
  --Create new settingsObject
  settings = settingsObject firstSetting:3 secondSetting:"something" thirdSetting:false
  iniFile = "C:/test.ini"
  
  --Write the settings to an ini file
  for i in (getPropNames settings) do (
  	attribute = (i as string);
  	value = ((getProperty settings i) as string);
  	setINISetting iniFile "Settings" attribute value;
  )
  
  --Create a new settingsObject
  newSettings = settingsObject()
  --Load the settings into the new settingsObject
  for i in (getPropNames newSettings) do (
   	attribute = (i as string);
   	value = getINISetting iniFile "Settings" attribute;
  	setProperty newSettings i value
  )
  

edit: You may want to split the for loop and the (getPropNames settings) part, that will -theoratically- speed up the script.

Hi guys thanks for all this writing and code for the explanations , going to read all carefully and try to apply to my scripts i will post what i find useful.

all this things with strctures and gloabl let me think about if it was a easy way to know the names and how many global you have running on max. like a command globallist …

and another question if you perform a gc() Do you will kill the globals and the global structure with them.

Bobo write this on the area as i create htread over there with the same tittle

http://discussion.autodesk.com/adskcsp/thread.jspa?threadID=551953


There is no simple answer to your questions – mainly because structures are
so versatile that they can be used for many things.

In short, structs allow you to create new value classes in MAXScript. Not as
fully featured as new object classes in C++, but still you can define
objects that have properties and functions and that you can create instances
of to represent some sort of abstract entity.

But as I mentioned already, the simplest use is to package functions.
You are sort of right that you could define functions “normally”, but there
are several reasons putting functions in a structure is a good practice:

In general, it is advisable to create as few global variables as possible.
If you define global functions, you are “polluting” the global scope. Any
variable in MAXScript is always searched for in the local scope, in any
higher scopes AND in the global scope. If you have thousands of global
variables (many of them function names), searching for a variable’s name
(incl. resolving a function’s name) can potentially become slower. (This is,
of course, theoretical, don’t know if it would be measurable). And then
there are the collisions…

The worse case is when multiple 3rd party develpers (say, you and me)
decided to call a function that does something obvious using the SAME
obvious name. Let’s say the function calculates the volume of a mesh and I
call it CalculateVolume(). Now I install some 3rd party script and its
author implemented his own global function that does (or does not) the same
but also has the same name. You wouldn’t even know of the name collision –
the script evaluated last would replace any previous versions of that
function and could potentially break any number of other scripts. The one
solution would be to keep the function local to the script, but what if both
authors wanted it to be a “library” function accessible by anyone? Here,
packaging functions developed by the same author or for the same project
under a common, UNIQUE name, like BobosMeshFunctions.CalculateVolume() makes
such a collision impossible because you would never come to the idea to call
a struct of yours BobosSomething… ;o)

So putting dozens of functions into a single struct requires JUST ONE
global variable containing the struct itself. On top of that, having to
prefix the function with a struct name also helps you make your code more
readable, because it makes it obvious what the function is meant for. For
example, MAXScript in R2 tended to expose each function as a global
function, so we got all these getVert(), getFace() etc. methods for
manipulating TriMeshes. Then, in R3 the meshOp. struct came along (it was
written in the SDK, but the principle is identical) and encapsulated all new
mesh-related methods. When in R4 the EPoly was added, a new structure called
PolyOp. was implemented – note that most of the function names were the
same, so you can just replace MeshOp with PolyOp and get about the same
functionality for your geometry type. If they were not in structs, the
function names would had to be completely unique to avoid collisions, or the
method would had to be made to operate on multiple data types, making the
functions potentially slower and more complex under the hood.

I personally use some structs to store only data. (For example, in the
Deadline Submitter for 3ds Max, all settings are kept in a struct). Such a
global struct instance containing parameters for some tool that you want to
have access to from any number of scripts is much better than an array,
because you are accessing the data BY NAME and not by index.

For example:

global SMTDSettings
struct SMTDSettingsStructure
(
Pools = #(),
PoolExclusive = false,
PoolName = “3dsmax”,
PoolsSort = false,
Priority = 50,
LimitEnabled = false,
MachineLimit = 0,
RenderOutOfOrder = false,
OutOfOrderMethod = #normal,
OutOfOrderStep = 10,
SubmitAsSuspended = false,
SubmitAsDependent = false,
AutoDelete = false,
ChunkSize = 1
)
SMTDSettings = SMTDSettingsStructure()


This is a small part of the Deadline Submitter set of job and submission
properties that is created as a global struct. When it is first called, all
properties are initialized to the default values you see on the right side.
Right after that I update some of them from an INI file to the latest
settings. Now I can get and set any property, for example to set a new
Priority, I can say SMTDSettings.Priority = 60 and the struct value will be
updated. The setting is done from the GUI script (which is separated from
the actual submission script) but I could as well type it in the Listener
since the name is obvious. When the submitter is preparing a job, it would
read that property and use the new value.
If another application at Frantic Films has to submit a job to Deadline, it
does not have to implement its own submission code or even run the main
submitter’s GUI – it only has to make sure the struct is initialized, change
some properties as needed and call the submission functions which read from
that struct. So I am using it as an intermediate storage in memory where the
GUI is reading and writing to it and the submission code is mostly reading
from. This way, any number of scripts can use the Deadline functionality
without reinventing the wheel.
If I were to use a global array to store this data, my scripts would have
been unreadable because instead of using property names, they would have
used indices.
Compare the above to

global SMTDSettings = #( #(), false, “3dsmax”, false, 50, false, 0, false,
#normal, 10, false, false, false, 1)

To get or set the Priority, I would had to use SMTDSettings[5], but in the
real script there are 3 pages of properties, so it would require a remark
next to each array element to remember what the 13th false value means…
;o)

Finally, structs are used to create new compound value classes – see the
Reference that shows you how the define a Person with age etc. This way, you
can abstract data that does not necessarily has anything to do with Max
scene objects or Max in general. For example, if you are scripting a
database of all your colleagues to be able to include email addresses and
cell phone numbers in an HTML spreadsheet for a Max-based project (I had to
do that once ;o), you could define a “colleague” class with properties like
.email, .cell, .ICQ etc. and then create instances of this class for each
entry you create. You could store these objects in an array as any other
values and get and set their properties like you do with properties of boxes
and spheres…
Because you can call getPropNames() anf GetProperty() on a struct instance,
it is also easy to store to disk by dumpling all properties prefixed by the
struct name and followed by assignment of their values to a MAXScript file.
Whenever you want to load that version of the struct, simply fileIn() the
file!

I haven’t even scratched the surface of the topic…

I’ve bbeen doing test and the gc(0 doesnt kil lthe globals the only got kill when max restart

after reading bobo reply i got a few more questions:

Sharing structures between diferent scripts

I was thinking if you want to share your structures as you explained between diferent script what is the easiest way for doing it.

Is better to get them with filein or include. In my case i got a folder in script/luigi/luigifunctions. Than normally i load my functions.

Or should be better to put the structure in the stdplugins/stdscripts so every time I launch max and we not need to include or filein in all the scripts.?

Is better use scripts/starup or stdplugins/stdscripts?

Save the structure in the max file

Apart of that I was using persistent global to keep my variables and Array in the max file.
So for do the same with structure should we have to declare the structure as persisent.

Another option I was seing like the onion script form blur , create a CA in the global tracks call the onion. Could a CA contain a structure where i store all my data relevant to the script.

And the third one as bobo was pointing is create a ini file than the structure save and load the dat from but this will create a ini file for each file and i will prefer to store the info in the max file and not in a external file

 PEN

I have all my structs that I have created stored in stdPlugs/stdScripts so that on start up they are run and are now sitting in global space ready to be called on.

Some of the ways that they are called are this.

By a macroScript that runs a function that might launch a UI for a tool. Reference PEN M Group from my site to see how I’m doing this.

Called from another script where I have common functions stored in a struct that are used my many other scripts. I have for instance a PEN_mathLib.ms that contains many needed math functions that are called by other scripts.

As bobo has pointed out there are many ways that you can use them.

Page 1 / 2