Flash for the masses – AS, JSFL, XUL, etc.

2010/02/05

Save Layers to File – Adobe Illustrator Javascript

Filed under: Adobe, Illustrator, javascript — delfeld @ 8:16 pm
Tags: , , , , , , ,

I think there would be a lot more work done through Adobe Illustrator Javascript if it did not fail in critical areas. There are two weak areas that I discovered, one which is not easy to find an answer, and one which seems like it is a bug.

Bug: referencing artboards

AI javascript prevents a reference to the artboards of non-active documents. So care is needed in some situations to make the correct doc the activeDocument in order to access the artboard. E.g., “ogDoc.artboards” ACTUALLY refers to “activeDocument.artboards” (though “ogDoc.layers” refers to “ogDoc.layers”).
So you have to set activeDocument to the dos that has the artboard you need. Simple, but REALLY frustrating for a while.

Bad design: naming a new file

When you create a new document, it starts out without a name. You have to give it a name, or else you will NOT be able to save it. Of course, you cannot just set the document’s name (or else I would not be writing this). Instead, you have to change the path. But you cannot just set the path = name. You have to use this function:

  • newDoc.path.changePath(filename);
  • This is the most important line of code to know for saving a new doc, and it took a long time to find it, since I did not know what exactly to search for. Who would guess that setting a file name is done only by setting the path, and no way else?

    With that out of the way, here is a gem of a Javascript. It will take all top-level layers in one file and copy all items in that top-level layer to another file, and save it as an SVG, PDF, or whatever else you might set up. The beauty of this script is that it preserves the layer structure, the layer names, and repositions according to the copied artboard.

    Please read this first – this code is not generic: This is designed for a document with multiple artboards, all with the size 1100×900, and the position of layer items is aligned to that artboard structure. You have to align your layer items with an artboard, and name your layer with the index number of the artboard to copy. This will be easy enough to change since it is well-documented throughout, but for now this Javascript is non-generic.

    // ==========
    
    // Create new files based on the top-level layers.
    // ASSUMES: The first two characters of a layer indicate the index of artboard that is supposed to show them.
    // Does not copy or export the layer (or sub-layer) if the layer's name begins with an underscore ("_").
    
    // ==========
    
    //alert( activeDocument.artboards.length );
    //alert( activeDocument.layers.length );
    //alert( activeDocument.artboards[ parseInt(activeDocument.layers[24].name.substr(0,2)) ] );
    
    var newDocDim = [1100, 900];
    var ogDoc = documents[0];
    
    saveLayersToFile(ogDoc);
    
    // ==========
    
    function saveLayersToFile(ogDoc)
    {
    	var ogLys;
    	var newDoc;
    	var nm;
    	var idx;
    
    	// For testing:
    	//for(  var i = 1; i >= 0; i--)
    
    	// Cycle through all top-level layers.
    	// Each top-level layer will be a new file.
    	for(  var i = 0; i = 0; j--)
    	{
    		// Skip this layer if the layer's name begins with an underscore ("_").
    		if( ogLyrs[j].name.substr(0,1) == '_' )
    			continue;
    
    		//alert(ogLys[j].name);
    
    		// Store locked state of the source layer.
    		// Note: Copying is allowed from a locked layer, but pasting to a locked layer is not.
    		isInitiallyLocked = ogLyrs[j].locked;
    		ogLyrs[j].locked = false;
    
    		// Create layer, and copy the layer name.
    		curLy = outLyrs.add();
    		curLy.zOrderPosition = 0;
    		curLy.name = ogLyrs[j].name;
    
    		// Duplicate items on a layer.  Does not duplicate sub-layers, though.
    		// As far as I can tell, this will grab all the items within a layer.
    		// However, there are a lot of potential things to grab, so I have included
    		// the rest of the references I found in Adobe's JS ref PDF below.
    		duplicateLayerItems(ogLyrs[j].pageItems, "pageItems", curLy, offsetRect);
    
    		//duplicateLayerItems(ogLyrs[j].graphItems, "graphItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].groupItems, "groupItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].meshItems, "meshItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].nonNativeItems, "nonNativeItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].pathItems, "pathItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].compoundPathItems, "compoundPathItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].placedItems, "placedItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].pluginItems, "pluginItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].rasterItems, "rasterItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].symbolItems, "symbolItems", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].textFrames, "textFrames", curLy, offsetRect);
    		//duplicateLayerItems(ogLyrs[j].legacyTextItems, "legacyTextItems", curLy, offsetRect);
    
    		// RECURSE: Create all sub-layers in the new doc, and copy the items.
    		if( ogLyrs[j].layers.length > 0 )
    			copyLayers(
    				ogLyrs[j].layers,
    				curLy.layers,
    				offsetRect
    			);
    
    		// restore locked state:
    		if( isInitiallyLocked )
    		{
    			ogLyrs.locked = true;
    			curLy.locked = true;
    		}
    
    		// For testing:
    		//break;
    	}
    }
    
    // ==========
    
    function duplicateLayerItems(items, nm, outLy, offsetRect)
    {
    	if( items.length  0 ) alert (
    		//"vals = "
    		//+ items[0].left + ","
    		//+ items[0].top + ";  "
    		//+ offsetRect[0] + ","
    		//+ offsetRect[1] + " _ "
    		//+ offsetRect[2] + ","
    		//+ offsetRect[3]
    	//);
    
    	var newItem;
    
    	// Copy all items.
    	for( var i = items.length - 1; i >= 0 ; i--)
    	{
    		// duplicate() is the best way to copy an unknown item type, but it is a bit slow.
    		newItem = items[i].duplicate(outLy); //, ElementPlacement.PLACEATEND);
    
    		// Reposition the items to the new doc's origin (0,0).
    		// This code is a bit of a fake, since I did not have time to work out
    		// why the position was offset the new doc's height.
    		// This may need tweaking in the future.
    		newItem.left = items[i].left - offsetRect[0]; // + newDocDim[0];
    		newItem.top = items[i].top - offsetRect[1] + newDocDim[1];
    
    		//alert( "pos = " + newItem.position[0] + ", " + newItem.position[1] );
    	}
    }
    
    // ==========
    

    Recurse through layers – Adobe Illustrator Javascript

    Filed under: Adobe, Illustrator, javascript — delfeld @ 7:53 pm
    Tags: , , , , ,

    One of the big problems I have had is trying to work with Adobe Illustrator layers. Editing layers is so manual that it really does take the bulk of my time if I have to do a lot of changes. This is a weak area where Adobe could improve the interface; but in the meantime, here is a couple Illustrator Javascript functions that will recurse through all layers, and run whatever function you send as a parameter.

    Please read the commented section in the code for more clarification, or post a comment and I will try to answer your questions.

    // ==========
    
    var ogDoc = documents[0];
    
    // Recurse through all layers, running the function passed as a parameter.
    // Extra parameter entry available if needed.
    // Note: Skips all layers where the layer's name begins with an underscore ("_").
    
    // Display the name of all layers, one at a time.
    //recurseLayers(ogDoc.layers, tellLayer, null);
    
    // Add an underscore to the front of any layer name that does not already start with an underscore.
    // Does not recurse into layers that already have an underscore, though; you can change the recurseLayers() function to do that.
    //recurseLayers(ogDoc.layers, initUndescoreLayerName, true);
    
    alert( "Edit this file to pick the function to run.");
    
    // ==========
    
    function tellLayer(lyr)
    {
    	alert( lyr.name );
    }
    
    // ==========
    
    function recurseLayers(ogLyrs, func, param)
    {
    	var curLy;
    	var isInitiallyLocked;
    
    	for( var j = ogLyrs.length - 1; j >= 0; j--)
    	{
    		// Skip this layer if the layer's name begins with an underscore ("_").
    		if( ogLyrs[j].name.substr(0,1) == '_' )
    			continue;
    
    		//alert(ogLys[j].name);
    
    		// Store locked state of the source layer.
    		// Note: Copying is allowed from a locked layer, but pasting to a locked layer is not.
    		isInitiallyLocked = ogLyrs[j].locked;
    		ogLyrs[j].locked = false;
    
    		func(ogLyrs[j], param);
    
    		// RECURSE: Crawl all sub-layers.
    		if( ogLyrs[j].layers.length > 0 )
    			recurseLayers( ogLyrs[j].layers, func, param );
    
    		// restore locked state:
    		if( isInitiallyLocked ) ogLyrs.locked = true;
    
    		// For testing:
    		//break;
    	}
    }
    
    // ==========
    
    function initUndescoreLayerName(lyr, turnOnUnderscore)
    {
    	alert(lyr.name + " ... " + lyr.name.substr(0,1));
    
    	// Add underscore.
    	if( turnOnUnderscore )
    	{
    		// Skip if there is already an underscore.
    		if( lyr.name.substr(0,1) == '_' )
    			return;
    		else
    			lyr.name = "_" + lyr.name;
    	}
    
    	// Remove underscore.
    	else
    	{
    		if( lyr.name.substr(0,1) == '_' )
    			lyr.name = lyr.name.substr(1, lyr.name.length);
    		else
    			return;
    	}
    }
    
    // ==========
    

    2009/12/21

    Event Listener Info

    Listener Repetition Prevention

    The same event type and listener combo will only be added once:
    For the following example code, when the mouse clicks on the stage, clickHnd() is only called once. And since there is only one listener for MouseEvent.CLICK, only that listener is removed in clickHnd().

    protected function setup():void
    {
    	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 1, true);
    	stage.addEventListener(MouseEvent.CLICK, clickHnd2, false, 2, true);
    	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 3, true);  // duplicate except for higher priority
    }
    
    protected function clickHnd2(e:MouseEvent):void
    {
    	trace("TestEventCount.clickHnd2");
    }
    
    protected function clickHnd(e:MouseEvent):void
    {
    	e.currentTarget.removeEventListener(e.type, clickHnd);
    	trace("TestEventCount.clickHnd");
    }
    

    I used e.currentTarget rather than e.target to remove the listener because the listener is on the stage, not on the clicked object. This is not always needed, and often currentTarget will give the wrong results; so know what you are calling, and why, or test it!
    See Adobe’s capture-bubble explanation for more details on target vs. currentTarget, and events in general.

    So you can repeat adding events to an object, and it won’t add any processing time except for the check to see if it already exists.

    Reference Type

    Note that the above event listeners use weak reference. This means that if the object that they are attached to gets deleted, then these listeners get deleted by the garbage collector.  Above, they are attached to the ’stage’ object; in use, though, often times they will be attached to some other foreign object. In order to make these listeners NOT get garbage-collected, attach them with strong reference:

    	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 1, false); // 5th parameter is set to 'false', not 'true'.
    

    Priorities

    Higher priorities (larger numbers) will get fired first. Simple! In the above code, clickHnd2() will be processed before clickHnd().  (There is a priority 3 event listener, but it is a duplicate, so doesn’t actually exist).


    Etc.

    If anything interesting (to me!) comes up about events, I will add on to this post.

    2009/12/01

    TextField JSFL fix

    Filed under: Adobe, Flash, JSFL — delfeld @ 5:51 pm
    Tags: , , , , ,

    Font embedding has been a problem in the past with Flash. I believe now most of the issues are fixed, though it is useful to set up TextFields using best practices.

    On the deleteaso blog, some JSFL code was posted that fixed various font embedding issues. This was a response to Seb Lee-Delisle’s idea of fixing fonts using AS3. Though using JSFL is an excellent idea, and the deleteaso code does work, it is pretty bad code:

    • an extra while loop (j-loop), which slows everything down. Fix: removed the loop.
    • a mis-placed if-statement; the code cannot get to the “static” if-statement via the code provided. Fix: break apart the if-statement.
    • should combine all matrix manips into one. Fix: None. I did not change this, since I do not want to debug/test it.
    • should scan all objects in the library that can contain other objects (it only scans MovieClips). Fix: add check for other DisplayObject types. Since I don’t use “button” item type, I only have added/tested the “graphic” item type. The other possible types are: “undefined”, “component”, “movie clip”, “graphic”, “button”, “folder”, “font”, “sound”, “bitmap”, “compiled clip”, “screen”, or “video”.
    • it is not ideal to switch static TFs to dynamic as a default condition. Fix: added a flag and continue-statement to control that feature.
    • very limited feedback for the user, and few comments within the file. Fix: obvious.
    • not sure why a while-loop was used instead of a for-loop. Fix: none . . . not a big deal

    Further, I wanted to add an improvement that allowed setting TextField defaults in all open FLA documents, not just one. This is the loop at the top of the code.

    If you use this code, note that it only affects TextFields found within the library. This will miss any TFs put directly on the stage – at this point, I have not changed that functionality.

    // This code modified by Delfeld, copyright 2009.
    // Modified code found at: http://delfeld.wordpress.com/2009/12/01/jsfl-textfield-fix/
    // Original code found at: http://deleteaso.com/fix-textfields-jsfl/
    
    fl.outputPanel.clear();
    
    //scanLibrary(fl.getDocumentDOM().library);
    
    var doChangeStaticToDynamic = false;
    var outStr = "Fixing textfields for proper output.\nNOTE: This will only affect objects held in the library (or instances of them).\n\n";
    var testDoc;
    
    // cycle through all open documents:
    for (doc in fl.documents)
    {
    	outStr += "Document: " + fl.documents[doc].name + "\n";
    
    	// set TextField defaults in this doc:
    	scanLibrary(fl.documents[doc].library);
    }
    
    fl.trace( outStr );
    
    // -------------------------------------------------------
    
    // auxillary function:
    function getIsWholeInt(n)
    {
    	var s = String(n);
    	if(s.indexOf(".") != -1)
    	{
    		return false;
    	}
    	else
    	{
    		return true;
    	}
    }
    
    // auxillary function:
    function round(n)
    {
    	var s = String(n);
    	var a = s.split(".");
    	var num = parseInt(a[0]);
    	var dec = parseInt(a[1].substr(0, 1));
    	if(dec >= 5)
    	{
    		num++;
    	}
    	return num;
    }
    
    /**
    * Scans the supplied flash library for linked classes and makes sure the textfields are set up properly
    * @param 	library		A flash library to scan.
    */
    function scanLibrary(library)
    {
    	// outStr += "Scanning textfields";
    
    	// loop variables:
    	var item, timeline, layerCnt, frameCnt, elems, elemCnt, matX, tx, matY, ty;
    
    	var items = library.items;
    	var replaceCount = 0;
    
    	// for each item in the library:
    	for( var i = 0; i < items.length; i++ )
    	{
    		item = items[i];
    
    		//outStr += "item: " + item.itemType + "\n"
    
    		// dig into any MCs:
    		if (item.itemType == 'movie clip' || item.itemType == 'graphic' )
    		{
    			// store current timeline:
    			timeline = item.timeline;
    
    			// store total number of layers:
    			layerCnt = timeline.layerCount;
    
    			// set up editing mode:
    			library.selectItem(item);
    			library.editItem();
    
    			// cycle through all layers within this timeline:
    			while(layerCnt--)
    			{
    				// store total frames
    				frameCnt = timeline.layers[layerCnt].frameCount;
    
    				// cycle through all frames within this layer:
    				while(frameCnt--)
    				{
    					// store elements array:
    					elems = timeline.layers[layerCnt].frames[frameCnt].elements;
    
    					// store total number of elements in this frame:
    					elemCnt = elems.length;
    
    					// cycle through all elements within this frame:
    					while(elemCnt--)
    					{
    						// process any text type element:
    						if( elems[elemCnt].elementType == "text" )
    						{
    							outStr += "\t Found a textfield: "
    							+ ((elems[elemCnt].name == "") ? "{no name}" : elems[elemCnt].name);
    
    							//Change the static TextFields to dynamic
    							if( elems[elemCnt].textType == "static" )
    							{
    								if( !doChangeStaticToDynamic )
    								{
    									outStr += ".  Ignoring this static TextField.\n";
    									//skip this iteration (element)
    									continue;
    								}
    								else
    								{
    									outStr += ". (Switching this static TextField type to dynamic.)\n";
    									// change text type to dynamic:
    									elems[elemCnt].textType = "dynamic";
    								}
    							}
    
    							outStr += "\t Text type: " + elems[elemCnt].textType + "\n";
    
    							if(elems[elemCnt].textType == "dynamic")
    							{
    								//Remove the ability to be selectable
    								elems[elemCnt].selectable = false;
    							}
    
    							// This section applies to all dynamic or input TextFields
    
    							// Remove the auto kern attribute
    							elems[elemCnt].setTextAttr('autoKern', false);
    
    							// Embed fonts UpperCase, LowerCase, Numerals, Punctuation
    							// This can be fine tuned to get a smaller font set.
    							elems[elemCnt].embedRanges = "1|2|3|4";
    
    							// Pop onto whole pixel
    							// not sure that two assignments are needed:
    							matX = elems[elemCnt].matrix;
    							tx = matX.tx;
    							if(!getIsWholeInt(tx))
    							{
    								matX.tx = round(tx);
    								elems[elemCnt].matrix = matX;
    							}
    							// Can this be combined with the 'x' section?
    							matY = elems[elemCnt].matrix;
    							ty = matY.ty;
    							if(!getIsWholeInt(ty))
    							{
    								matY.ty = round(ty);
    								elems[elemCnt].matrix = matY;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    }
    

    2009/07/28

    Arbitrary MovieClip rotation – by Tim Whitlock

    Filed under: AS3.0, Adobe, Flash — delfeld @ 3:32 pm
    Tags: , , , , , , ,

    I am keeping this on my blog only to be able to not lose it.

    The explanation of this class is at: http://web.2point1.com/2008/04/13/scale-rotate-around-an-arbitrary-centre, and any usage must be credited to that blog’s owner, Tim Whitlock.

    /**
     * File containing class example.VirtualCentreSprite
     * @author Tim Whitlock
     */
    package example { 
    
       import flash.display.Sprite;
       import flash.geom.Point;
       import flash.geom.Matrix; 
    
       /**
        * Simple sprite class that supports scaling and rotating around an arbitrary centre point
        */
       public class VirtualCentreSprite extends Sprite { 
    
          /**
           * Scale around an arbitrary centre point
           * @param Number local horizontal offset from 'real' registration point
           * @param Number local vertical offset from 'real' registration point
           * @param Number relative scaleX increase; e.g. 2 to double, 0.5 to half
           * @param Number relative scaleY increase
           */
          protected function scaleAround( offsetX:Number, offsetY:Number, absScaleX:Number, absScaleY:Number ):void {
             // scaling will be done relatively
             var relScaleX:Number = absScaleX / this.scaleX;
             var relScaleY:Number = absScaleY / this.scaleY;
             // map vector to centre point within parent scope
             var AC:Point = new Point( offsetX, offsetY );
             AC = this.localToGlobal( AC );
             AC = this.parent.globalToLocal( AC );
             // current registered postion AB
             var AB:Point = new Point( this.x, this.y );
             // CB = AB - AC, this vector that will scale as it runs from the centre
             var CB:Point = AB.subtract( AC );
             CB.x *= relScaleX;
             CB.y *= relScaleY;
             // recaulate AB, this will be the adjusted position for the clip
             AB = AC.add( CB );
             // set actual properties
             this.scaleX *= relScaleX;
             this.scaleY *= relScaleY;
             this.x = AB.x;
             this.y = AB.y;
          } 
    
          /**
           * Rotate around an arbitrary centre point
           * @param Number local horizontal offset from 'real' registration point
           * @param Number local vertical offset from 'real' registration point
           * @param Number absolute rotation in degrees
           */
          protected function rotateAround( offsetX:Number, offsetY:Number, toDegrees:Number ):void {
             var relDegrees:Number = toDegrees - ( this.rotation % 360 );
             var relRadians:Number = Math.PI * relDegrees / 180;
             var M:Matrix = new Matrix( 1, 0, 0, 1, 0, 0 );
             M.rotate( relRadians );
             // map vector to centre point within parent scope
             var AC:Point = new Point( offsetX, offsetY );
             AC = this.localToGlobal( AC );
             AC = this.parent.globalToLocal( AC );
             // current registered postion AB
             var AB:Point = new Point( this.x, this.y );
             // point to rotate, offset position from virtual centre
             var CB:Point = AB.subtract( AC );
             // rotate CB around imaginary centre
             // then get new AB = AC + CB
             CB = M.transformPoint( CB );
             AB = AC.add( CB );
             // set real values on clip
             this.rotation = toDegrees;
             this.x = AB.x;
             this.y = AB.y;
          }
       }
    }
    

    2009/07/07

    Your very own Sound Event!

    Filed under: AS3.0, Adobe, Flash — delfeld @ 3:55 pm
    Tags: , , , , , , ,

    Events are the core of actions in Flash. Often, though, the Events provided fail to provide the some information or command needed.

    Say, for instance, a you have a number of sound files loading and playing, and want to manage them within another class. You want to listen for events that happen to the Sounds as well as the SoundChannels, the Event.SoundComplete event, and not have to have a complicated way of getting to the Sound in question. For that, you can set up a new Event to handle Sound events from various sources.

    In the example below, I have taken a standard Event and added the ability to pass a Sound object with the event. I have also added a few more event types (EA_AUDIO_ADDED, etc.). I would have to set up where and when to fire the new event types; however, up to this point, I did not have access to some of these functions at all.

    To use, set up a dispatchEvent within the sound loader class, the sound playing class, or wherever you are getting a sound from. For example, within a class called (for example) MyOwnSoundPlayer:

    dispatchEvent(new EventAudio_test(someAudioObject, EventAudio_test.EA_SOUND_PLAY_COMPLETE, false, false));

    Then you set up a listener attached to that class instance (this would be in another class, probably, though it doesn’t have to be – a class can listen for it’s own events, too), which references a function somewhere (usually this is in the same class, but an event listener can reference any accessible method or function):

    var plyr:MyOwnSoundPlayer = new MyOwnSoundPlayer();
    plyr.addEventListener(EventAudio_test.EA_SOUND_PLAY_COMPLETE, functionToRun, false, 0, true);

    And the called function would look something like this:

    protected function functionToRun(e:EventAudio_test):void
    {
    	var audioRef:Sound = e.audio;
    	// Sound work here using audioRef . . .
    }
    

    So here is the full EventAudio_test class:

    package AS3_0.classes.Events // Put your own package here.
    {
    	// Load whatever you need.  This is set up for sound files:
    	import flash.media.Sound;
    	import flash.events.Event; // Import the Event class.
    
    	public class EventAudio_test extends Event // Extend Event to get it's functionality
    	{
    		// Create a bunch of global String constants for events that you need.
    		// The constant's name should indicate the usage.
    		// I also like to have identical text as the name, so I can't make a stupid mistake.
    		static public const EA_AUDIO_ADDED:String = "EA_AUDIO_ADDED";
    		static public const EA_SOUND_LOADED:String = "EA_SOUND_LOADED";
    		static public const EA_ALL_CHANNELS_CLEARED:String = "EA_ALL_CHANNELS_CLEARED";
    		static public const EA_SOUND_PLAY_COMPLETE:String = "EA_SOUND_PLAY_COMPLETE";
    
    		// This is a new variable that holds whatever you want passed within the event through it's parameters.
    		// An Event is only a function, so you can have as many variables as you wish, and can pass Functions, Arrays, Objects, etc.
    		private var _audio:Sound;
    
    		public function EventAudio(audio:Sound, type:String, bubbles:Boolean = false, cancelable:Boolean = false)
    		{
    			// Store the reference:
    			this.audio = audio;
    			// Finish the Event constructor:
    			super (type, bubbles, cancelable);
    		}
    
    		public override function clone():Event
    		{
    			// Note that the variable needs to be added to the clone() method:
    			return new EventAudio(audio, type, bubbles, cancelable);
    		}
    
    		public override function toString():String
    		{
    			// Note that the variable needs to be added to the toString() method:
    			return formatToString("audio", "type", "bubbles", "cancelable", "eventPhase");
    		}
    
    		// Allow public access to the variable via getter/setter methods:
    		public function get audio():Sound { return _audio; }
    		public function set audio(value:Sound):void
    		{
    			// Not necessary, but is always good to reduce duplication, especially if you start using extensive objects:
    			if (_audio !== value)
    			{
    				_audio = value;
    			}
    		}
    	}
    }
    

    2009/06/24

    What designers need to know to use ActionScript 3.0 . . .

    1. All things that exist on the stage (even if invisible or inactive) are Classes.
    2. All actions that happen are Events.
    3. All Events are Classes.
    4. All Classes inherit the Object class.

    The only things that are different are:

    Primitive data types:

    • Boolean
    • int
    • Null
    • Number
    • String
    • uint
    • void

    These act quite a bit like classes in many use examples.

    Complex data types:

    • Object
    • Array
    • Date
    • Error
    • Function
    • RegExp
    • XML
    • XMLList

    Each of these have some specialized create, read, update, and delete (CRUD) techniques, so do not expect them to be consistent between each other. For example, compare the different ways to create an XML object and an Array object.

    You can review this data type explanations at Adobe’s data type description page.

    That’s it . . . though there may be more to come.

    2009/06/04

    Order of ops for ‘Record Pivot Rotation’ and ‘Parent in Place’ in Lightwave

    Filed under: Lightwave 3D — delfeld @ 9:14 pm
    Tags: , ,

    This is such an important concept to what I am working on, I cannot let it get lost in the ether. I have not tested this in Lightwave’s lScript, but I suspect it is the problem I have been having with automatically adding bones. Ergo, here it is, lifted straight from Gary Creighton’s website http://www.angelfire.com/wizard/gandalf27/layout1.html:

    “I continue, reparenting all other skelegon chains to ‘Deinon_Root’ with ‘Parent in Place turned on. I finish each bone’s reparenting by pressing ‘r’ to rethink that bone’s ‘Rest Positon’ before I move on to the next bone.

    “You should always check to make sure your object looks correct. Since you won’t generally be reparenting more than about 5 or 6 bones, you might turn on visibility on the object after each bone to ensure that nothing is twisted, then save the scene with a new name because Layout has no undo (or one level of undo for limited operations).

    “Then… turn visibility off on the object once more to reparent the next bone.

    IMPORTANT – ‘Parent in Place’ ONLY WORKS IF YOU HAVE NEVER ROTATED THAT BONE BEFORE.

    “Therefore, you must reparent bones BEFORE you record their rest positions. If you follow exactly what I said to do, and bones twist your object? Most likely you had already rotated those bones away from their original positions with ‘Record Pivot Rotation’, and thus ‘Parent in Place’ isn’t working.

    “So, only ‘Record Pivot Rotation’ AFTER you’ve reparented bones (This is like building the house of cards, one-by-one. Beware that the house of cards is very senstive to the order in which you place those cards).”

    2009/06/02

    MovieClip Frame Life Cycle

    Filed under: AS3.0, Adobe, Flash — delfeld @ 3:10 pm
    Tags: , , , , , ,

    I am taking no credit for this post. It is directly derived from Johannes Tacskovics blog post: http://blog.johannest.com/2009/05/22/the-movieclip-life-cycle-event-frame_constructed-and-event-exit_frame/
    He also offers a more complete description of the events. I only need to make sure that I have the reference to the life cycle, so here it is:

    1. Event of event type Event.ENTER_FRAME dispatched.
    2. Constructor code of children MovieClips is executed.
    3. Event of event type Event.FRAME_CONSTRUCTED dispatched.
    4. Frame actions are executed.
    5. Frame actions of children MovieClips are executed.
    6. Event of event type Event.EXIT_FRAME dispatched.
    7. Event of event type Event.RENDER dispatched.

    2009/05/06

    XML Quick Reference

    Filed under: AS3.0, Adobe, Flash — delfeld @ 5:27 pm
    Tags: , , , ,

    I will add to this list as I find a need, or as my memory plagues me:

    • xml.attributes()[idx] – Treating attributes, children, comments, descendants, elements, text, etc. as an array. The original output is an XMLList.
    • xml.attribute(“*”)[idx] – Treating attribute, child, comments, and others that use parameters as an array. The original output is an XMLList.
    • name() – Retrieving the name of the current XML object – the name of an attribute, element, or other XML non-data names.
    • xml.attributes()[idx].name() – Retrieving the name of an attribute.
    • xml.someElementName.(@id == marker).child(“*”) – Retrieve all children of all elements whose “id” attribute match the “marker” variable.
    • xml.elements(“someElementName”) – Retrieve an element by the element’s name. Returns a new XMLList of the element and it’s children.
    • Make an array out of XML.
    • length() – Get number of entries from some XML object (Array uses length (no parentheses), which is confusing at times)
    • A simple for loop looks like this:

      if ( xmlBase
      	&& xmlBase.someElement
      	&& xmlBase.someElement.multipleElementsArea
      	&& xmlBase.someElement.multipleElementsArea.length() > 0
      )
      {
      	trace("all elements = " + xmlBase.someElement.multipleElementsArea);
      
      	var val:int = xmlBase.someElement.multipleElementsArea.length();
      	for (var i:int = 0; i < val; i++)
      	{
      		trace( "some attribute value: " + xmlBase.someElement.multipleElementsArea[i].@attribute);
      		trace( "some element value: " + xmlBase.someElement.multipleElementsArea[i]);
      	}
      }
    • xml.entryItem.(@attribute1 == “someValue”).@attribute2[indexInt] – This returns data stored in an attribute contained within the same element as the searched attribute.
      Returns the value stored in an attribute named attribute2 within an XML element entryItem, where initially the list is sorted by attribute1. Useful in a for loop. This is similar to a horizontal lookup command in a spreadsheet.
    • var xml:XML = <elementName attr1={someVariable} attr2={someOtherVariable}>{variableForTheText}</elementName> – Using variables in implicit XML. Adds attributes and element text via variables. Note: Quotation marks are automatically added to attributes (around someVariable and someOtherVariable), but not added to element info (variableForTheText).
    Next Page »

    Blog at WordPress.com.