Mapping coordinates from one range to another in ActionScript 3.0

I am very fond of Processing, and the way it easily makes it possible to experiment with design ideas. One of the powerful features are it’s map() function, that takes a value and remaps it from one range to another. I haven’t seen a similar function in the ActionScript 3.0 reference, so I decided to make one to my own utils-library, because it is something the can come in handy in many situations. First I looked at using the Point() class, and interpolation to describe the ratios, but actually found it easier to just do it with regular math.

I haven’t looked at performance yet, and there is possibly a lot of ways to optimize this code – this article can be a start for further fine tuning. The helper function looks like this:

function map(val:Number, inMin:Number, inMax:Number, outMin:Number, outMax:Number, decimals:uint=0):Number
{
var returnVal:Number;
 
// Sets the total range for both in and out
var inRange:Number = inMax - inMin;
var outRange:Number = outMax - outMin;
 
//calculates the ratio for the value in relation to the in range
var ratio:Number = (val - inMin)/inRange;
//makes sure that the value is within range
val = Math.max(inMin, val);
val = Math.min(inMax, val);
//Finds the same ratio in the out range and shift it into place
returnVal = (outRange * ratio) + outMin;
 
//rounding of the decimals
var factor:int = Math.pow(10,decimals);
returnVal = Math.round(returnVal*factor)/factor;
 
return returnVal;
}

The function takes the the following arguments:

  • val: the number to be remapped
  • inMin and inMax: The original range, in which the value is measures in relation to.
  • outMin and outMax: The destination range, that the value will be re-mapped to
  • decimals: sets the precision in decimals (optional)
First it calculates the ranges with
var inRange:Number = inMax - inMin;
var outRange:Number = outMax - outMin;
These variables are defining the total range for both the incoming number, and the range of number is eventually will be placed within.
val = Math.max(inMin, val);
val = Math.min(inMax, val);
In this function I have decided to make sure, that the value is within the boundaries of the inRange. If i took the value 4, and sets the range of number to be within 10 and 100, I would get some unexpected results. You could treat this differently, or even make it possible by omitting these lines.
var ratio:Number = (val - inMin)/inRange;
ratio holds a number between 0 and 1 that defines where in the original range it is located. The closer to zero it gets, the lower the value – closer to one goes towards inMax.
returnVal = (outRange * ratio) + outMin;
Then the number is multiplied to the outRange to find the same relative point in the target range, and finally shifted into place by adding the outMin value. Negative values are treated fine in these ranges – when you add -200 to something, it is actually subtracted from the number.
var factor:int = Math.pow(10,decimals);
returnVal = Math.round(returnVal*factor)/factor;
The last part of the function is meant to round of the values. For performance, this could easily be removed, but it may be useful in some cases to have odd long numbers rounded and presented with only a few digits precition. First, the variable factor holds the value to be used in the rounding proces. For 1 digit it should be “10″, 2 digits is “100″, 3 is “1000″ and so forth. That value is used in the line below, where it first multiplies the final value by the factor and then rounds it to an integer. Then it divides with the factor again. It goes something like this:
  1. factor is 1000 (3 digits)
  2. value is 134.744637
  3. Multiplication results in 134744.637
  4. Rounding returns 134745
  5. divided by 1000 gives the result 134.745
Finally the value is returned as a Number.

Optimization

There are a lot of code shortening, that can produce a speed improvement. You could also work with int or uint exclusively to gain some performance i suppose. Finally, you can remove the decimals and the lines that check if the number is in range. I have left the to show what functionality that could come in handy.

An example

As a practical example of the map() function you can  copy the following code into a Flash document. Here the map() fuction is directly inside the main code, but you could start building your own utils-library with the helper functions as public static function for easier access.
The code draws two squares with different sizes. When the mouse moves over the small square, it draw lines in the larger square, while maintaining the relative coordinates inside the squares.
import flash.display.Sprite;
import flash.events.MouseEvent;
 
//make the small square and color it dark grey
var inArea:Sprite = new Sprite();
inArea.graphics.beginFill(0x333333);
inArea.graphics.drawRect(0,0,100, 100);
inArea.graphics.endFill();
addChild(inArea);
 
//make the large square and color it black
var outArea:Sprite = new Sprite();
outArea.graphics.beginFill(0x000000);
outArea.graphics.drawRect(0,0,400,400);
outArea.graphics.endFill();
outArea.graphics.lineStyle(1, 0xFFFFFF, 0.3);
addChild(outArea);
outArea.x = inArea.width;
 
//adds a listener for the small square, that listens for mouse movement
inArea.addEventListener(MouseEvent.MOUSE_MOVE, updateDrawing, false, 0, true);
function updateDrawing(event:MouseEvent):void
{
	//Map the local coordinates from the mouse movement to the size of the large square
	var targetX:int = map(event.localX, 0, event.target.width, 0, outArea.width);
	var targetY:int = map(event.localY, 0, event.target.height, 0, outArea.height);
 
	//Draws the line in the large square.
	outArea.graphics.lineTo(targetX, targetY);
}
 
//Helper function
function map(val:Number, inMin:Number, inMax:Number, outMin:Number, outMax:Number, decimals:uint=0):Number
{
	var returnVal:Number;
	// Sets the total range for both in and out
	var inRange:Number = inMax - inMin;
	var outRange:Number = outMax - outMin;
 
	//makes sure that the value is within range
	val = Math.max(inMin,val);
	val = Math.min(inMax,val);
 
	//calculates the ratio for the value in relation to the in range
	var ratio:Number = (val - inMin) / inRange;
 
	//Finds the same ratio in the out range and shift it into place
	returnVal = (outRange * ratio) + outMin;
 
	//rounding of the decimals
	var factor:int = Math.pow(10,decimals);
	returnVal = Math.round(returnVal*factor)/factor;
 
	return returnVal;
}

Calling a function dynamically in ActionScript 3.0

Have you ever wondered, how you could use a String to decide, which function to be called. You could go so far to let the user enter the function name in an input field and call a function by that name, if it exists.

I have made a short article with an example on how that is done in Flash and ActionScript 3.0. You can read the article at http://www.hjaelpmignu.dk/content/dynamic-call-functions

Google Squared – en interessant søgemulighed

Jeg stødte i dag på Google Squared. Den ligger i deres laboratorium. Der ligger jo i forvejen en masse interessante ting, så vad er det lige der adskiller Squares fra det andet halløj.

Google Squares giver dig mulighed for at inddele din søgning i rækker og kolonner, så du kan få et skema over resultatet. En søgning på Countries gav følgende:

Resultat af søgning på "countries"

Resultat af søgning på "countries"

Google Squares opdeler det i relevante kolonner med navne, beskrivelser, hovedstæder osv. Hvis du vil have en specifik kolonne til at være synlig skal du bruge hårde parenteser i stil med “countries [popuplation]“. På den måde vil resultatet blive i stil med dette:

resultat med en specifik kolonne defineret

resultat med en specifik kolonne defineret

En anden ting, der er interessant er at det nye resultat giver nogle andre indledende kolonner. Den kigger derfor resultatet igennem og finder ud af, hvilke kolonner der er relevante i søgeresultet.

TIP: Du kan selvfølgelig også skrive en kolonne i Add Columns, i resultatet.

Denne form for søgning giver et helt andet overblik end den normale søgning, og det bliver nemmere at opsamle og sidestille delementer af resultater i forhold til hinanden. Alle skolepligtige vil helt sikkert kunne nyde godt af værktøjet. Jeg får i hvertfald  mange sjove søgeaftener med denne utitlity … men igen. Jeg er også lidt underlig :-)

Google Squared er stadig et eksperiment, så der er selvfølgelig mange tilføjelser man kunne ønske sig … meeen. Den er kommet godt ud af starthullerne. Hurra for Google

Prøv værktøjet på http://www.google.com/squared – god fornøjelse.

/ockley

Mit håndikon er væk!! – husk det nu

OK. Her kommer et indlæg til jer, som ligeså meget er for min egen skyld. Jeg sad i aftes og brændte rigtig mange unødvendige arbejdstimer af på noget jeg havde løst før. Nu skriver jeg det her på blog’en, så jeg selv og I aldrig falder ned i det frustrationshul igen :-)

Problemet er følgende:
Når jeg opretter et movie clip og vil have det skal opføre sig som en knap, kommer hånden ikke frem, når musen bevæger sig hen over det.

Umiddelbar løsning er:
Husk at sætte buttonMode til true. Når du gør det, kan du bruge useHandCursor til at bestemme om hånden skal vises eller ej. Prøv følgende:

//Opret en sprite. Placer den på din stage i position (50,50)
var knap:Sprite = new Sprite();
addChild(knap);
knap.x = knap.y = 50;

//Tegn en grøn kasse inde i knappen
var kg:Graphics = knap.graphics;
kg.beginFill(0x55CC55);
kg.drawRect(0,0,90,25);
kg.endFill();

//Aktiver knaptilstand for spriten
knap.buttonMode = true;

Den sidste linje får spriten (eller movie clippet) til at opføre sig som en mus. Nu kommer problemet. Hvis du skriver følgende efter:


//Opret et tekstfelt, hvor teksten ikke kan markeres
//og placer den inde i knappen
var tekst:TextField = new TextField();
tekst.autoSize = TextFieldAutoSize.LEFT;
tekst.selectable = false;
tekst.text = "Nu er der tekst i";
tekst.x = tekst.y = 5;
knap.addChild(tekst);

Når du tester filmen, vil du se at tekstfeltet fjerner musehånden fra knappen. Hvis du flytter markøren helt ud til et af hjørnerne vil du se, at den stadig er aktiv – det er tekstfeltet, der er skyld i misæren.

Der er to måder at løse det på. Begge bunder i at det ikke skal være muligt for objekter inde i symbolet at reagere på musens gestus. Hvis du vil forhindre tekstfeltet i at forholde sig til musen kan det gøre således:

tekst.mouseEnabled = false;

Dette vil påvirke den enkelte knap og skrives tit samtidig med oprettelsen af tekstfeltet (når man husker det) :-)

Den anden metode fjerner muligheden for alle objekter inde i knappen. Det er yderst praktisk, hvis knappen er bygget op af mange delobjekter, som du med sikkerhed kan sige, ikke skal bruge musen til noget. Med andre ord: Hvis det kun er knappen som helhed, der skal reagere. Det ser således ud:


knap.mouseChildren = false;

Den sidste er værd at huske på. Men grundlæggende virker det forkert at musehånden ikke virker, når bare teksten ikke er markerbar. Må dette indlæg spare dig for noget bøvl, hvis projektet skulle dreje sig i den retning :-)

/ockley

Redigering af keyboard shortcuts i After Effects CS4 (og CS3)

Der er nogle ting der bare er nemmere, når man er amerikaner, eller fra et andet engelsksproget land. Det er når man skal praktisere diverse genvejstaster i Adobes programmer. Der er utrolig mange, som er uundværlige, enten fordi de er nemmere end den normale kommandovej, eller også fordi du med tasterne kan udføre ting, som ellers ikke er muligt via menuen eller paneler. En af de taster er dem, der flytter in- og out points til den aktuelle position i laget og dem der trimmer in- og out point til den aktuelle position i laget.

På et engelsk tastatur er det de hårde klammer ([ og ]) henholdsvis uden- og med ALT tasten nede. De ligger oppe til højre for tasten P. Den luksus har vi desværre ikke. Derfor er andre tastasturer nød til at redefinere keyboard shortcuts der ligger uden for normalen. Programmer som Photoshop har fantastiske editorer til den slags, men ikke Adobe After Effects CS4.

Det har undret mig at muligheden ikke var der, og min bedste mulighed har ind til videre været at redigere i filen “Adobe After Effects 9.0 Shortcuts.txt”, der findes i mappen “c:/documents and settings/[bruger]/Application Data/Adobe/After Effects/9.0/” (Windows) eller “[bruger]/Bibliotek/Preferences/Adobe/After Effects/9.0/” (Mac OS X)

Bøvlet ville ingen ende tage, indtil jeg stødte på et script kaldet “KeyEd Up” og er i en samlet pakke med en række andre scripts. Du kan hente det på Exchange til After Effects CS3 og After Effects CS4

Som udgangspunkt kan du pakke filerne ud og placere dem i Script mappen. Den er placeret i samme mappe som programmet på Mac. På Windows skal du lige inde i mappen “Support files” for at finde den. Opret eventuelt en egen mappe til dem kaldet “nye scripts” e. lign.

Når du starter After Effects op igen, vil du kunne fange dem fra undermenuen File > Scripts

En sidste detalje inden du kan bruge scriptet er, at du er nød til at give After Effects lov til at skrive til filer på din computer. Det gør du således:

  1. Vælg Edit>Preferences>General (Windows) AfterEffects>Preferences>General (Mac OS)
  2. Sæt flueben i “Allow Scripts to Write Files and Access Network”
  3. Afslut med OK

Nu kan du vælge editoren via File>Scripts>KeyEd Up.jsx

For at rette sådan en problem som trim in og trim out kan du gøre følgende med editoren aktiv

  1. Vælg Hvilken kategori din shortcut hører til under Category (her er valgt “Time”)
  2. Vælg den kommando du vil redigere genvejdstasten til under Commands (her er valgt Trim Layer In point …)
  3. Vælg den  nye genvej fra sektionen i højre side, Tilføj eventuelt kontroltaster (her er valgt “Option + I”)
  4. Klik eventuelt på Show Usage for at se eventuelle konflikter.
  5. Tryk på Update Shortcut for at ændre din genvejstast.HUSK: Hvis du vælger en anden kommando uden at trykke på Update shortcut først, glemmer den den valgte ændring.
  6. Tryk OK og genstart After Effects før ændringerne træder i kraft.
Shortcut editor til After Effects

Shortcut editor til After Effects

P.S: Nogle af dine scripts kan blive indlejret i After Effects som paneler, hvis du flytter dem til mappen. Der er tale om Active Shutter, Folder Setup, Launch Pad og Swatch You Want. Du kan placere dem, sammen med deres PNG-fil i mappen ScriptUI, så vil du kunne få fat i dem fra Windows menuen.

God fornøjelse

/ockley