flex.scoutant.org


 

2010/02/26

Un "Hello 3D World" en Blender, animé en Flash avec Papervision3D

Démo

Passez la souris sur l'objet 3D ci-dessous...

You need JavaScript enabled!

Ouai, c'est donc un Hello World pour valider l'intégration du modeleur 3D Blender avec la librairie Papervision3D pour application Flash.

Avec aussi animation de rotation 3D et le calcul d'ombres temps réel qui va avec...

Modélisation Blender et export au format Collada

Bon, quelque soit le modeleur 3D qu'on utilise (3ds Max, Maya, SketchUp, Blender), au départ il y a un petit temps d'apprentissage.

Avec blender.org, on est en plein dans le développement durable : logiciel libre, multi-plateforme. Ya plein de doc en ligne et pour n'en citer qu'une : http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro.

Après s'être familiarisé aux divers raccourcis clavier et techniques de modélisation comme l'extrusion, on peut construire sa première scène 3D.

Ensuite on peut exporter au format COLLADA. Un format d'échange standard en XML. Avec Blender, c'est simplement File>Export>Collada 1.4. On cochera les options :

  • Triangles
  • Only export selection

Effectivement notre librarie Papervision 3D fonctionne exclusievment par modélisation à l'aide de triangles.

Import dans appli Flash avec librairie Papervision 3D

Mise en place du projet Flex avec Papervision3D

Donc maintenant on est dans Eclipse avec typiquement Flash Builder 4. Enfin moi je préfère Flex Builder 3 / Linux. Ya plus qu'à télécharger Papervision3D : http://code.google.com/p/papervision3d. Au choix directement la librairie .swc ou alors le .zip avec le code source.

Pour explication des concepts de base de papervision3d on pourra juste lire l'article : first-application-with-papervision3d.

Effectivement il faut saisir le pourquoi de la source lumineuse et de la caméra et voir ou les placer par rapport à l'objet cible.

Un modèle de projet évoqué dans la partie "smart programmers use less code", servira de base. Ca peut servir de base, par exemple :

public class LegoWithLightAndShader extends BasicView {
    public function LegoWithLightAndShader() {
      super(0, 0, true, false);
      init();
    }
 
private var model:DisplayObject3D;
    private function init():void {
    opaqueBackground = 0xffffff;
      camera.x = 0;
      camera.y = 400;
      camera.z = -550;
 
      light = new PointLight3D(true);
      light.x =  100;
      light.y = 1000;
      light.z = -700;
Import du modèle .dae
private var model:DisplayObject3D;
		model = new DAE();
		(model as DAE).load("assets/MyLegoSample.dae");

Donc on le voit, l'import est très simple. Suffit de sourcer le fichier .dae.

Annimation de rotation via la souris
private var vpX:Number = stage.stageWidth/2;
    private var vpY:Number = stage.stageHeight/2;
    override protected function onRenderTick(event:Event=null):void {
      var angleX:Number = (mouseY - vpY) * .5;
      var angleY:Number = (mouseX - vpX) * .5;
      model.rotationZ = -angleY;
      model.rotationX = -angleX;
      super.onRenderTick(event);
    }

Oui, on peut choisir d'effectuer une rotation du modèle sur la base de la distance de la souris par rapport au milieu de l'écran. Maintenant, on a une animation correcte, mais le résultat est beaucoup trop médiocre.

You need JavaScript enabled!

Effectivement, on n'a pas de gestion d'ombres : les arrêtes ne se démarquent pas et on ne voit de distinction entre les faces. Alors que les yeux et le cerveau ont l'habitude de reconstituer le 3D justement via des modifications d'intensités lumineuses entre les faces de l'objet...

Gestion des ombres

Matériaux et import Blender -> Papervision

On peut avoir fait une modélisation Blender avec des matériaux présentant réflexion et réfraction. Mais à l'import du .dae, ceci est perdu et le modèle papervision3d instancié ne présente que des matériaux ColorMeterial. Donc sans calcul d'effet de lumière!

Choix des matériaux dans Papervision3D

Au prix d'un surcoût CPU, on peut induire papervision à une gestion fine de la lumière. Au lieu de ColorMaterial, on optera pour :

  • FlatShadeMaterial
  • GouraudMaterial
  • PhongMaterial

Pour le premier, il s'agit d'un calcul global pour chaque face (triangle) de l'objet par rapport à la source lumineuse. p3d-light

Le dernier est beaucoup plus réaliste (et couteux) avec une approche pixel par pixel. Le deuxième est un compromis : calcul triangle par triangle et interpolation pixel par pixel.

Pour notre Hello 3D World, très cubique, la première approche est parfaite.

Remplacement des materiaux en runtime

Reste à remplacer le ColorMaterial introduit lors de l'import par une instance de FlatShadeMaterial.

model.addEventListener(FileLoadEvent.LOAD_COMPLETE,modelLoaded);
 
	private function modelLoaded(e:FileLoadEvent):void		{
   		model.replaceMaterialByName(  new FlatShadeMaterial(light, 0xFFEEFF, 0x220000), "RED");
	}

Il suffit d'utiliser la méthode replaceMaterialByName(). Et de remplacer le matériaux dans le dictionnaire relatif à l'objet. Tout simplement!

On trouve plusieurs posts qui font état d'un fonctionnement étrange de Papervision (en tout cas jusqu'à 2.1). On peut tenter d'instancier un MaterialList, d'ajouter le matériau cible et de remplacer l'ancien via model.materials = ... Mais ceci n'a pas d'effet. Pourquoi?

Bon, on n'a pas toujours réponse à tout. L'essentiel est d'avoir une solution qui marche.

C'était la démo présentée en introduction.

Avec ce Hello 3D World, on a vu le chemin d'intégration Blender -> Papervision 3D. Incluant animation de rotation et surtout gestion réaliste de la lumière.

Sculpture numérique et visite virtuelle

On pourra maintenant sereinement retourner à Blender et faire un peu de sculpture numérique. On sait qu'on pourra ensuite exposer son chef d'oeuvre dans une simple application Flash, permettant au public une découverte sous tous les angles...

Si on fait une modélisation avec lissage, on préfèrera alors la mise en oeuvre d'un PhongMaterial, pour un calcul pixel par pixel de la lumière et un rendu des plus réalistes.


 

2009/10/28

FlexUnit4 and Flash Builder 4 in action - A FlexUnit4-power TDD Use case

FlexUnit4 Use case : a Reverse Polish Notation Calculator

You need JavaScript enabled!

A nice introduction is the Wikipedia page on Reverse Polish Notation [1].

In Reverse Polish notation the operators follow their operands; for instance, to add three and four, one would write "3 4 +".

Let's play with the calculator above. It is a live Flash application.

Type some digits and press ENTER so as to push a number to the stack. When you have two numbers in the stack you can hit a binary operator key like '+' or '*'. The 2 operands will be consumed and the result will be push to the stack. As an option you can use keyboard shortcuts.

The requirements are easy to express. And present use case is prone to a test-driven development approach. And in our simple case, it is realy close to feature-driven development.

Test-driven development

Googling, you'll find many references and even books on TDD.

To make it short , the point is to write the test code before the target code.

So we have micro development cycle :

  • adding a test method and do the least for the test to compile
  • run the unit test and notice that it fails : RED state in the unit-test report panel
  • implement the target logic : you are not finised until the unit test succeed : GREEN state.

All the unit test are independent one from each other. So that they can be cumulated in unit-test campain that are to be run before any code is commited.

With that test automation infrastucture, you can now feel confident to refactor often your code : within some seconds you can have feedback in case of any regression.

But what about TDD for AS3 and Flex developpement?

ActionScript and Flex TDD out there?

It's true, there are not that many reference on test-driven development when talking about GUI.

Okay you will find the FlexUnit page @ Adobe Dev Net [2]. And some other references.

But FlexUnit is not integrated to Flex Builder, so that very few project have adopted a true TDD approach.

Next generation FlexUnit4 and Flash Builder 4 enabling true AS3/Flex TDD

Compared to Flex Builder 3, Flash Builder 4 comes with a major feature : a true unit testing support. With both :

  • TestCase class generation wizards and
  • the possibility to run a TestCase from the IDE without the bowler-plate of defining any test suite nor test app.

FB-wizard2

Moreover, after (too) many years of FlexUnit 0.90, Adobe has at least decided to put some effort in a next generation unit-test framework. We have a upcoming FlexUnit4 framework [3].

As you can see, it's an adaptation of JUnit4, the annotation-driven unit-test java support. Comprehensive illustration following at this post : http://www.insideria.com/2009/05/flashbuilder4-will-support-fle.html .

A true development workflow sample will surely best describe the benefits of the soon to be released tdd support with Flex4 ecosystem.

FB4-flexunit4

Today we will use the available releases : Flash Builder 4 beta 2 and FlexUnit4 beta 2 turnkey distribution. I suppose FlexUnit4 will be fully integrated to the IDE, but with beta release, a last step is necessary o set up the environnement.

FlexUnit4 Turnkey distribution is a Flex4 project you can import with Flash Builder 4 Import wizard.

A step by step true TDD sample : writing the AS3 test cases before the target code

Let's write AS3/Flex code meeting the RPN Calculator requirements. Let's create a Flex projet, called rpn. The TDD-style drives us to first write the TestCase. Let's do so using the 2 wizards mentionned above.

Create an AS3 TestCase class using wizard

Let's instanciate a Calculator from the FlexUnit4-powered CalculatorTest class below :

package org.scoutant.rpn {
	public class CalculatorTest {	
		protected static var calculator:Calculator = new Calculator();
		}
	}
}

Notice how our test case does not need to inherit any test case base class (as with FlexUnit 0.90). Obviously, it won't compile :

tdd-2

We need to define the Calculator class.

Create now the target class

JUnit4 integration within Eclipse is somewhat more powerfull : the quick-fix feature suggest the Java developer the generation of corresponding class. With Flash Builder 4 beta2, we'll have to do it manually. But this is not a big deal.

package org.scoutant.rpn {
	public class Calculator {
		public function Calculator() {
		}
	}
}

Okay, now it compiles. We can begin the the iterative tdd workflow at method level.

Create a FlexUnit4 test method

Writing unit-test method code

Let's consider the single following feature : pressing the digits composing a number and when done : validating with an enter action. And notice how the number is now on top of the stack. An AS3 represnetation of that could be :

[Test]
public function enteringAnOperand(): void {
  calculator.enter( 3);
  Assert.assertEquals( 3, calculator.peak());
}

The test method name does not matter. With FlexUnit4 it does not need to be prefixed by test. What matter is the Test metadata. Just like JUnit 4 is based on Java annotations.

Now we have the unit test. It does not compile :

If needed adding the API code for the test to compile

Let's just define the mentionned enter(.) and peak() method.

public function enter(operand : Number) : void {
}
public function peak(index:int = 0) : Number {
  return NaN;
}

Now it compile. We can run the test. To do so with beta2 release, we need to select the test case in the explorer view : the contextual menu, with right click, shows Executes FlexUnit Tests. Of course the test fails. But we have bow the test infrastucture up and we can focus on implementing the target logic.

Implement the logic meeting that small piece of requirment.

A little bit of design : the Calculator requirement are somewhat similar to a Last-in first-out stack:

  • pushing items,
  • popping the two top-most items so as to operate them

Hence, our Calculator class may be based on an AS Array. Yes, the Array API comes with push(.) and pop() methods.

private var stack : Array = [];
public function enter(operand : Number) : void {
	stack.push( operand);
}
public function peak(index:int = 0) : Number {
	return stack[ index ];
}

Now we run the test and it turns to GREEN.

We are done with the feature. BUT a unique test for a feature may not be enough a validation.

a second unit-test method for same feature

[Test]
public function enteringTwoOperandsAndReadin(): void {
	calculator.enter( 3);
	calculator.enter( 6);
	Assert.assertEquals( 6, calculator.peak());
}

The test above test that the number that is pushed last, is actually retrieved by peak() method. The test compiles, let's run it to verify that the test passes.

tdd-4

Fixing the bug

It is not expected, the test fails! we have the RED bar. We must fix that before considering any other feature.

Notice the advantage of our tdd approach and it's micro cycles. The bug introduced by the implementation above is discovered with a minute of time!

We know that the cost of a bug is exponential to the time elapsed since it was intoduced. Here our tdd approach will minimize that cost.

public function peak(index:int = 0) : Number {
	return stack[ stack.length -1 - index ];
}

Yes the AS3 Array has a stack API : push(.) and pop(). But no peaking feature. Our first implementation was not LIFO-consistent.

After refactoring the index as above. We can run the test-case. We can see how the last test turns GREEN.

We can now consider next feature, for instance adding two operands.

TDD of next feature : adding two operands

implementing a test method

Let's enter 2 operands and hit the "+" operator. We want the 2 last operands to be consumed and the sum to be push into the stack. A test may be :

[Test]
public function addingTwoOperands():void {
	calculator.enter( 3);
	calculator.enter( 6);
	calculator.add():
	Assert.assertEquals( 9, calculate.peak() );			
}

For the test to compile we need to define the methode add() .We can begin with an empty method body. The test compiles.

We run it and we have the RED state.

! implementing the logic 
public function add():void {
	stack.push( stack.pop() + stack.pop() );
}

With the above implementation our test turns to GREEN. tdd-5

Expressing an espected Error with FlexUnit4

As in previous cycle we'll add more test to validate the target add() method.

The "+" operator is assuming to find two operands in the stack. If none or only one is available we want the calculator to raise an error.

The GUI shall give an error feedback the the user.

Compared to FlexUnit 0.90, FlexUnit4 provides an interesting feature. The possibility to express that a test is supposed to raise an error.

[Test (expects="Error")]
public function addingButNoOperand():void {
	calculator.add();			
}

We are mentionning that we expect an error: the test case will be green if we actually have the error. Let's run the test. tdd-6

Fixing the bug

We expect our add() method to raise an error. But corresponding assertion (expects="Error") is not validated.

We need to refactor our add() implementation. Looking att the AS3 Array reference documentation, we discover that popping an empty array does raise an Error but silently returns null.

We are not happy with that so let's add an applicative precondition and raise an ArgumentError if the stack has less that 2 operands.

public function add():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( stack.pop() + stack.pop() );
}

Now , running the test, yes it is GREEN. Let's add a second test, just to be sure that if we none operand, we do have an error too:

[Test (expects="Error")]
public function addingButNoOperand():void {
	calculator.add();			
}

The test case is still GREEN.

More tests for same feature

Lets add a test case with negative number and mixing integrer and Numbers...

[Test]
public function addingNegativeOperand():void {
	calculator.enter( -3);
	calculator.enter( -6);
	calculator.add();			
	Assert.assertEquals( -9, calculator.peak() );			
}
[Test]
public function addingIntegerAndNumberOperand():void {
	calculator.enter( 3);
	calculator.enter( 6.1);
	calculator.add();			
	Assert.assertEquals( 9.1, calculator.peak() );			
}

Still GREEN. we can can confidently consider implementing next feature.

TDD of similar feature (multiply operand) : copy and paste both the test fixture and the business logic

We can feel that the "*" operator is very much similar to the "+". Both are binary and symetrical. The code can be very similar. We can copy and paste both the test methods and the target logic.

But to prevent any mismatch, best is to abide to the RED before GREEN principle. So we can copy and adapt the test fixture. And verify the no compilation or the RED state.

TDD of next feature (substract operand)

Operating as above, we first provide the test fixture mentionning a substract(). Compile error. And by similarity we provide following implementation :

public function substract():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( stack.pop() - stack.pop() );
}

We just replaced "+" by "-" in the implementation. We can run the test.

tdd-7

Fixing the bug

It's NOT green. We must correct the implementation! The Substract operator is not symetrical ; and obviously the add template has to be adapted : Let's replace with :

public function substract():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( - stack.pop() + stack.pop() );
}

Now it turn to green. Thanks to our TDD approach we corrected the bug no later that 30 seconds after the bug was introduced.

Let's go on with division operator.

Next feature ( division operator)

The operator is not symetrical and we'll take care.

[Test]
public function divideTwoOperands():void {
	calculator.enter( 8);
	calculator.enter( 2);
	calculator.divide();
	Assert.assertEquals( 4, calculator.peak() );			
}

Let's implement the missing divide() method. It is a binary and orientated operator like the substract one. Let's take it as template :

public function divide():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	var dividend:Number = stack.pop(); 
	stack.push( stack.pop() / dividend );
}

Here no surprise, it's GREEN.

Next feature ( unary square root operator)

unit-test fixture and target implementation
[Test]
public function squarreAnOperand():void {
	calculator.enter( 1024);
	calculator.sqrt();
	Assert.assertEquals( 32, calculator.peak() );			
}

Not compiling. We add en empty sqrt() implementation. RED state.

We may consolidate with following implementation.

public function sqrt() : void {
	if (stack.length < 1) {
		throw new ArgumentError();
	} 
	stack.push( Math.sqrt( stack.pop() ));
}

Now it turns GREEN.

Additional fixture for square root of negative number

Let's add an additional test fixture. For a negative number we want an error (we are not onsidering complex number).

[Test (expects="Error")]
public function squareRootNegativeNumber():void {
	calculator.enter( -1);
	calculator.sqrt();
}

Result. tdd-8

Fixing the expectation

It is NOT green. We expected an Error.

But diving into the AS3 doc, we can see that Math.sqrt() calculation actually does not raise an error for negative nymbers : it just returns NaN...

Let's manually raise the expected Error.

public function sqrt() : void {
	if (stack.length < 1) throw new ArgumentError();
	if ( this.peak() < 0 ) throw new ArgumentError();
	stack.push( Math.sqrt( stack.pop() ));
}

Now it's GREEN.

Now our Calculation logic seems to be finished.

Next step is to wrap it with the GUI components.

GUI wrapping

We can now develop the GUI against our unit-test Calculator class. Not only the Calculator class is tested but moreover the feature are clear. We have a somewhat a feature-driven approach.

Now we can take advantage of one of Flex4 main feature : the FXG skinning architecture. We can first develop a squetch GUI based on simple buttons.

Micro architecture

We have the Calculator.as logic.

We'll implement a rpn.mxml application. Our RPN Calculator mostly has a sreen representing the stack and buttons : digits and operator.

Bas practice is to consider all the GUI stuff in a single .mxml file. Even more when targetting Flex4 FXG skinning feature.

We'll at least consider Digit.mxml an Operator.mxml files.

Component layout

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
  xmlns:s="library://ns.adobe.com/flex/spark" 
  xmlns:rpn="org.scoutant.rpn.*"
  >
<s:layout>
  <s:VerticalLayout/>
</s:layout>
<fx:Script>
  <![CDATA[
  [Bindable] public var calculator:org.scoutant.rpn.Calculator = new org.scoutant.rpn.Calculator();
  [Bindable] public var line:String = "";
  ]]>
</fx:Script>
. . .
<s:SkinnableContainer width="100%">
  <s:layout>
    <s:HorizontalLayout/>
  </s:layout>
  <rpn:Digit id="_7" label="7"/>
  <rpn:Digit id="_8" label="8" />
  <rpn:Digit id="_9" label="9" />
  <rpn:Operator id="DIVIDE" label="/" operator="{calculator.divide}" />
  <s:Button label="1/x" />
</s:SkinnableContainer>
. . .

Connecting the logic to the GUI : with method inection

<rpn:Operator id="DIVIDE" label="/" operator="{calculator.divide}" />

The line above is instanciating a component (named Operator.mxml here). And we are injecting an AS3 method : {calculator.divide}.

The operator.mxml component is a normal Flex Button : the click handler will execute the operator.

The operator may be defined as a AS 3 variable of type Function. So that the function maybe injected as seen above.

<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"  xmlns:s="library://ns.adobe.com/flex/spark" 
  click="execute()"
  >
  <fx:Script>
    <![CDATA[
      [Bindable] public var operator:Function;
      public function execute():void{
        operator();
      }
    ]]>
  </fx:Script>
</s:Button>

Above we could he logic beeing invoqued with a user gesture : hiting the "DIVIDE" button invoques the Calculator divide() method.

As a feedback we expects now the "sreen" to be updated with the new stack content. Flex data-binding is the best friend for that!

Connecting the GUI to the logic : with data-bindig

We cannot use the peak() method for the data-binding. We need to bind to a specfic property or accessor. It could be :

<s:Label textAlign="right"  text="{''+calculator.stack[calculator.stack.length-1]}" />

Here the compiler warns us. tdd-9

I would say an other way : the Array class is not an EventDispatcher.

Hence we may refactor the Calculator class. We can wrap the Array with an ArrayCollection that is an EventDispatcher.

public class Calculator {		
  [Bindable] public var lifo:ArrayCollection = new ArrayCollection();
  private var stack : Array = lifo.source;

Now we can express the data-binding with getItemAt(.) expression. Just like the compilatior Quick Fix suggested.

<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-1)}" />

Now the user has the desired feedback. HP 28 S Calculator [4] has a 4-line screen, in a bottom up fashion. Could be :

<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-4)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-3)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-2)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-1)}" />

Now we have a functionnal RPN Calculator.

You need JavaScript enabled!

It was not the point in present post, but all the components used here inherits from Flex 4 Spark SkinnableComponent. In next post, we'll see how to write Skin mxml components. That can be referenced to as to have a nice RPN Calculator. With very few changes to our application, only specifying the skinClass property for our Skinnable Compoents...


 

2009/04/27

Hosting a PHP proxy and enabling Flash app to talk cross-domain

velov-proxy

FlashPlayer and cross-domain policy file

FlashPlayer offre une politique de sécurité rassurante : une application Flash chargée depuis un domaine A ne pourra consommer des services web depuis un autre domaine B.

Il est possible de configurer cette politique de sécurité. Considérons que le domaine cible B juge qu'il est opportun de servir les requêtes issue d'une appli Flash téléchargée depuis le domaine A. Alors il suffit que B expose, à la racine, un fichier crossdomain.xml qui alloue les droits correspondants.

Pour exemple, Delicious expose un fichier qui donne accès à toutes les appli Flash téléchargées depuis n'importe où : http://feeds.delicious.com/crossdomain.xml. Si vous suivez le lien, il faut éventuellement Afficher le code source.

<cross-domain-policy> 
  <allow-access-from domain="*" /> 
</cross-domain-policy>

C'est ce qui a permis de faire l'animation Flash d'un nuage de tags Delicious, DeliSphere, a Flash 3D-animation tagcloud for your Delicious bookmarks .

Google, Youtube, Fickr et d'autres exposent également une politique Flash-friendly : par exemple : http://api.flickr.com/crossdomain.xml .

Un Proxy !

Maintenant, si le provider de service n'est pas aussi accueillant pour les application Flash, on a la possibilité d'implanter un service de proxy qui prendra en charge de relayer les requêtes avec une autre technologie de client. On implante un tel service dans le même domaine qui sert l'application Flash : vis-à-vis de la VM FlashPlayer, on n'est plus dans une situation de cross-domain.

Proxy en Java avec BlazeDS

Adobe propose pour cela une solution toute prête. BlazeDS [1], la solution open-source de remoting AMF3, intègre un service de proxy.

Coté développement Flex, il suffit alors tout simplement de positionner sur les divers objets de communication la propriété useProxy en conséquence.

<mx:HTTPService url="" useProxy="true" />

BlazeDS est la meilleure solution que l'on retiendra dans tous les cas industriels. Techniquement, il s'agit d'une librairie Java, ce qui suppose donc un hébergeur en Java...

Proxy en PHP pour les hébergements restreint au PHP

Ce présent blog est hébergé chez OVH et comme c'est souvent le cas l'hébergeur propose du Php mais pas du Java. Une solution, consiste alors à écrire un service de proxy en PHP.

C'est ce qui est mis en oeuvre par le démonstrateur Velib en Flash

<mx:HTTPService id="phpProxiedService" 
  url="http://flex.scoutant.org/php/proxy.php" method="GET"
  resultFormat="e4x" useProxy="false" 
  result="doLoad(event)" fault="faultHandler(event)"/>
public function start( marker:StationMarker):void {
    phpProxiedService.request.url = this.url + marker.station.num;
    var token:AsyncToken = phpProxiedService.send();
    token.marker = marker;
 }

Ici l'objet de communication, le HTTPService, pointe vers le service de proxy, en l'occurrence : http://flex.scoutant.org/php/proxy.php. Ce service fait maison propose l'API simpliste suivante : la requête doit présenter un paramètre url indiquant l'url cible.

Voici ci-dessous le code du service proxy, avec utilisation d'un client curl, pour lequel on positionne l'url cible via le paramètre de requête $_REQUEST['url'].

<?php
$ch = curl_init();
$timeout = 30;
$userAgent = $_SERVER['HTTP_USER_AGENT'];
 
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
 
$response = curl_exec($ch);     
 
if (curl_errno($ch)) {
    echo curl_error($ch);
} else {
    curl_close($ch);
    echo $response;
}
?>

On pourra se référer au billet Using a PHP Proxy with Flex to talk Cross Domain , pour un peu plus de détails.

Proxy en BlazeDS sur hébergement GoogleAppEngine?

Le service d'hébergement GoogleAppEngine a été étendu à la technologie Java [2].

Du coup, on peut utiliser BlazeDS? Je n'ai pas fait le test, mais le billet suivant fait un constat négatif : GoogleAppEngine and BlazeDS  : la package java.lang.management n'est pas autorisé...

Notes

[1] Adobe open Source : BlazeDS

[2] Google App Engine : The new language on App Engine


 

2009/02/ 5

Velib en Google Maps API For Flash - un MakerManager pour des performances!

Vous cherchez un vélo en libre service? velib.paris.fr vous présente les stations avec la disponibilité des vélos et la disponibilité des emplacement libre pour le retour. C'est une appli JavaScript qui utilise l'Api historique de Google Maps.

On peut faire l'équivalent en Flex en utilisant Google Maps API For Flash.

You need JavaScript enabled!

On peut aussi en faire une version full screen, voir la démo.

Dans ce cas, il faut faire attention aux performances : on n'ap as envie d'avoir d'à-coups quand on déplace la carte. Mais a Paris il y a plus de 1000 stations Velib ; et une grande Google Maps avec 1000 markers c'est saccadé!

Pour ca, la solution est l'utilisation d'un MarkerManager disponible dans une librairie complémentaire à la Google Map API. Il s'agit de la Google Maps Utility Library également disponible en version AS3 : gmaps-utility-library-flash.

Ce gestionnaire accroit fortement les performances, car seuls les markers qui sont dans la viewport sont réellement gérés.

L'utilisation est très facile, on instancie un Manager attaché à la map et ensuite les markers on les ajoute au gestionnaire plutôt que directement à la Google map.

mgr = new MarkerManager(map, null);

Quand on dé-zoom, on a naturellement plus de marker dans la zone visible. Le MarkerManager permet de se protéger de ceci : quand on ajoute un marker à la map, on précise à partir de quel zoom il devient visible.

L'exemple suivant parle de lui-même : pour une carte météo nationale, une seule icone soleil suffit pour une région et c'est seulement quand on zoom dans une région qu'i lest intéressant d'afficher les marqueurs relatifs à une météo plus locale. L'idée d'un nombre constant d'informations quel que soit le niveau de zoom est intéressante.

Justement quand on ajoute un marker, on est amené à préciser l'intervale de zoom pour lequel le marker est visible, parexemple :

mgr.addMarker(marker, 16, 17);

Vois avez sans doute cliqué sur l'un des markers : ca vous affiche la disponibilité de la station. Dans un prochain billet, un focus portera sur l'invocation du service web qui permet d'optenir les infos de disponibilité.

Ouai, mais on voit déja poindre les intérrogations sur comment est alors traité la politique de cross-domain. Vous pouvez déja lancer un sniffer réseau sur la démo ou aussi attendre le prochain billet.


- page 1 of 3