Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox
Chantier suffisamment avancé pour recueillir vos points de vue
Page non encore sécurisée.
Port du casque recommandé.

Les mystères du feuilleteur (Pageflip)

Par nataly (Nataly), le 01 avril 2011


L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.


Bonjour toutes et tous, qui vous dites depuis un moment :
“Mais bon Sang, comment ça marche c'te chose ?! Certainement faut-il être très fort en maths et encore plus en AS pour avoir une chance d'y comprendre quoi que ce soit…”.

Et bien non :) Et je suis formelle !
Une fois l'illusion démontée, je me fais forte de débrouiller l'embrouille à destination du plus grand nombre.

Aussi vais-je m'y prendre en deux parties :

Ici décortiquer le principe de base, puis en faire une fonction qui permet de corner et tourner une seule feuille depuis un coin quelconque, comme illustré ci-dessus.
Là-bas un exemple de mise en œuvre de la dite fonction au sein d'un feuilleteur complet.

Il sera donc, dans cette partie de tuto, question principalement de géométrie.
Ne fuyez pas, je promets que ça ne fait appel à rien qui dépasse l'entendement commun. Si vous savez reconnaître un angle droit, vous êtes sauvés. D'ailleurs je vais me permettre, à la demande générale du ronchon de service, un préambule aussi court que possible pour synthétiser et résumer les quelques points de vocabulaire indispensables et les deux trois formules que vous avez peut-être oubliés.

D'un point de vue AS3, je considère que vous en avez une pratique courante, on utilisera par exemple la classe Timer sans explications spécifiques. C'est d'ailleurs la seule chose à connaitre en dehors des traditionnels addChild, new Shape() et autres structures de contrôle.
Pour vous rafraichir les idées, si besoin était, vous trouverez un chapitre qui résume les choses à l'occasion de ce tuto.



Remerciements appuyés à :
Dldler et Lilive1), sans le secours desquels je ne suis pas bien certaine que ce tuto aurait abouti :)



Dans la vraie vie



Attaquons sans plus attendre, et une fois n'est pas coutume, ce n'est pas en créant nouveau document qu'on va commencer. Je vous propose de faire un détour par la vraie vie pour comprendre de quoi il retourne (oui : jeu de mots :mrgreen:).
Munissez vous de deux feuillets suffisamment rigides, papier cartonné, canson un peu lourd, bristol, ce que vous avez sous la main. Moi ce sera du bristol.



[video Mettre en évidence superposition et distances identiques : isocèle]

pour les aventureux impatients qui ne peuvent attendre l’inauguration, vidéo indispensable à visionner elle illustre les deux ruses fondatrices de tout le reste
Ce qu'on vient de faire avec nos petits doigts agiles - je ne doute pas que vous vous y soyez essayés aussi pour bien vous convaincre du principe - transformé en clips et en animation, ça donne ça (depuis le coin bas droit) :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.
Pour terminer l'illusion, il s'agira de se débrouiller pour qu'on ne voit qu'un morceau de Verso (le triangle du coin) et pour dissimuler ce qui “déborde” sur Recto, comme illustré au dessus avec majuscule enfoncée.
Il va donc être essentiellement question de triangles.

Afin d'avoir l'esprit libre pour jouer les Sherlock Holmes et débusquer les infos (angles et coordonnées) qui nous intéressent, je propose à ceux qui n'ont gardé que de très lointains souvenirs des années collège, un détour récapitulatif des connaissances que nous mettrons à contribution.


Le minimum à savoir pour suivre ce qui va suivre



Donnez moi un angle droit et je soulèverai le monde, c'est ma version toute personnelle de la célèbre saillie de Monsieur Archimède.
En effet, pour peu qu'on repère un angle droit quelque part on a toutes les chances de pouvoir calculer ce qu'on veut.

J'ai pris le parti de raisonnements géométriques. Il en est d'autres, sans doute plus élégants, peut-être même plus courts en terme de lignes de code, qui font appel à des connaissance mathématiques plus avancées.
Pour être accessible au plus grand nombre (et ne pas déborder de mes champs de compétence :mrgreen:), j'ai choisi l'option géométrie/trigo, en utilisant les quelques outils de première nécessité, soit trois fois rien.
Il s'entend que d'autres calculs permettraient d'obtenir le même résultat, et que les “formules” ici proposées ne sont pas, et de loin, les seules possibles. D'ailleurs en fin de tuto, je vous proposerai trois façons d'aborder et de résoudre la même problématique, charge à vous plus tard de choisir les outils mathématiques que vous privilégierez pour parvenir à vos fins.


Ce chapitre résume et synthétise les quelques points de géométrie sur les quels nous appuierons nos raisonnements. N'hésitez pas à sauter à la suite, on n'y verra rien qui concerne spécifiquement le développement du feuilleteur.

Triangle rectangle



Qui dit angle droit dit triangle rectangle, un peu de vocabulaire pour être sûr d'être bien d'accord :


Le coté face à l'angle droit se nomme hypoténuse (les grecs pensaient dessous)
Pour chacun des deux autres angles, on nomme opposé le côté face à l'angle considéré, et adjacent le dernier côté.






• Depuis Pythagore on sait que le carré de l'hypoténuse vaut la somme des carrés des deux autres côtés, écrit autrement :

H2= F2+G2

En gros, pour peu qu'on ait la mesure de deux côtés on trouve le troisième ;)

• Il est à noter aussi qu'un rectangle découpé par sa diagonale donne deux triangles rectangles identiques (mêmes angles, mêmes cotés).



Sinus cosinus et tangente



Soit un angle quelconque au sommet d'un triangle rectangle (oui, encore), on en obtient le sinus en calculant le rapport du côté opposé à l'hypoténuse ; le cosinus c'est côté adjacent sur hypoténuse, et tangente c'est opposé sur adjacent.




• Si on a un triangle rectangle, un côté et un angle… On obtient les deux autres côtés. Enfin, pour peu qu'on connaisse le sinus, le cosinus ou la tangente de l'angle en question.
Avec AS3 ce sont les fonction Math.sin, Math.cos et Math.tan qui s'en chargent.

• Si on a un triangle rectangle, deux côtés… On obtient les deux autres angles. Cette fois à l'aide des fonctions Math.asin, Math.acos ou Math.atan2).

Voilà, j'ai tout dit de ce qui nous occupera, ça vous rassure ? Parfait, alors une dernière précision pour être tout à fait au point :

Triangle isocèle



C'est ainsi qu'on appelle les triangles dont deux côtés sont égaux (même taille). Je les aime bien parce qu'on peut toujours les couper en deux de façon à obtenir deux triangles rectangle (ah ah).

Notez qu'on peut tracer une ligne qui part du milieu de la base (segment rouge) et rejoint le sommet. Chacun des points qui la constituent se trouve à égale distance des extrémités du segment rouge, on dit que c'est sa médiatrice3).
Elle a pour autre particularité de former un angle droit avec le segment concerné et dans un triangle isocèle elle définit un axe de symétrie. Du coup les angles de la base sont égaux.




http://debart.pagesperso-orange.fr/college/triangle_college_classique.html#ch1




Cette fois c'est bon, on sait où on va (calculer des coordonnées ou des angles), avec quoi (des triangles dont on sait tout) et d'où on part (le point qui suit les coordonnées de la souris). Défouraillez donc un nouveau document flash, c'est parti !

Depuis le coin en bas à droite

On va commencer par expérimenter depuis un seul coin puis on déclinera sur les trois autres. Je choisis arbitrairement le coin bas/droit (puisqu'il faut que je fasse tout).

Préparer le terrain


Quand il s'agira d'un feuilleteur tout entier (plusieurs pages réunies en “livre”) ces pages seront probablement des fichiers (.jpg, .png, .swf…) mais peut-être aussi des clips présents dans la bibliothèque d'un .fla.
Pour nous entrainer (et ne pas nous compliquer avec le chargement d'images) considérons deux clips de bibliothèque : un symbole P1(pour le recto) et un autre P2 (pour le verso).

Le clip Recto (P1)

Rien de compliqué : un malheureux rectangle, un champ texte (même pas obligé, mais ça permet d'y voir clair), le point d'origine en bas à gauche (parce qu'il faut bien convenir de quelque chose).

Le clip Verso (P2)

Pas beaucoup plus dur : un rectangle aux mêmes dimensions que celui du symbole Recto (obligé : on considère que toutes les pages sont de hauteur/largeur identiques), et un champ texte.
Le point d'origine en bas à gauche aussi - et là ce n'est plus indifférent du tout.

Ici j'ai un problème : soit je n'anticipe rien du tout et j'attends qu'on se prenne le mur, soit je m'égare en de longues justifications, peut-être prématurées… (pas facile ma vie, je vous le dis !)
Je coupe la poire en deux :


Ceux qui veulent tenter l'aventure sans filet peuvent passer à la suite.

• Les autres… Ayez confiansssssss… :mrgreen:

On va regrouper les éléments du visuel du symbole P2 en un clip. Le plus simple c'est de tout sélectionner, de convertir en symbole (Clip tant qu'à faire) à l'aide de la touche F8 en prenant soin de fixer le point d'origine en bas à gauche :



Vérifiez au passage que les coordonnées de l'occurrence sont bien en 0/0.


\\

Ajouter les pages à la scène


Une dernière formalité : ajouter une instance de P1 et de P2 sur la scène. Ou plutôt, pour faire facile/malin tout de suite, ajoutons ces instances dans un clip contenant, qu'on pourra disposer où bon nous semble.
En route :

Sur les sources jointes j'ai profité d'avoir un .fla à ma disposition pour répartir le code sur plusieurs calques (ça facilite pour s'y retrouver). J'ai donc un calque nommé “Declare init” et un un autre nommé “code”. Faites bien comme vous le sentez, mais prenez soin - si vous optez pour les calques différents - à ce que declare init soit au dessus des autres.


• Calque Declare init :

\\
// un contenant
var contenant:MovieClip= new MovieClip();\\
// les instances
var _recto:P1= new P1();\\
var _verso:P2=new P2();\\
\\
contenant.addChild(_recto);\\
contenant.addChild(_verso);// doit être au dessus de recto\\
// contenant où on veut comme ça nous arrange
contenant.x= 250;\\
contenant.y= 350;\\
addChild(contenant);\\
\\
var _haut:Number=_recto.height;// Récupérer la hauteur d'une page
var _larg:Number=_recto.width;// Récupérer la largeur
\\
_verso.x=_larg;// à droite de recto
_verso.rotation =120;// incliné arbitraire en attendant
\\



Suivre le pointeur


Ayé, on peut commencer, vraiment.
D'abord ce qu'on veut c'est que _verso suive le pointeur.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Pour se mettre en jambes, ce n'est pas le plus difficile. Convenons d'une fonction placePage qui sera invoquée de façon récurrente (via un Timer) quand on maintient le bouton de la souris enfoncé.

• Calque Declare init :

\\
contenant.addEventListener(MouseEvent.MOUSE_DOWN,demarre);\\
stage.addEventListener(MouseEvent.MOUSE_UP,arrete);\\
\\
var _tm:Timer=new Timer(50);\\
_tm.addEventListener(TimerEvent.TIMER,placePage);\\
\\
function demarre(me:MouseEvent):void {\\
	_tm.start();\\
}\\
function arrete(me:MouseEvent) {\\
	_tm.stop();\\
}\\




• Calque code :

\\
function placePage(e:Event=null):void {
	var x_c:Number=contenant.mouseX;
	var y_c:Number=contenant.mouseY;
	_verso.x=x_c;//  suit point courant
	_verso.y=y_c;
}\\


On se doute bien qu'on va avoir besoin par la suite des coordonnées du pointeur, autant les sortir tout de suite sous forme de variables x_c et y_c4).


Dessiner le point



Pour s'y retrouver plus tard ajoutons un point rouge (un vrai, un objet Shape) qui suivra le pointeur.

• Calque Declare init :

var scoin_c:Shape= new Shape();// coin courant (suit souris)
scoin_c.graphics.beginFill(0xFF0000);
scoin_c.graphics.drawCircle(0,0,5);
contenant.addChild(scoin_c);
\\
\\
function placePage(e:Event=null):void {
[]
	// dessin - - - - - - - - - - - - - - - - - -
	scoin_c.x= x_c;// suit point courant
	scoin_c.y= y_c;
}



Ça c'est fait :)

Déterminer l'angle de rotation du verso


L'énigme de l'angle vert



Maintenant réfléchissons : voici ce qu'on veut obtenir (hors lignes de construction)

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.


Et voici les données de l'énigme :)

Je vais les reprendre et avancer dans un des raisonnements possibles à coup de croquis. Je vais faire plus que détaillé et étape par étape dans l'idée certes d'être aussi claire que possible, mais aussi de donner, en quelque sorte, des indices successifs à ceux qui voudraient jouer et trouver “tous seuls” ;)





Reprenons donc sur un croquis :
Ce qu'on cherche : l'angle marqué en vert.
Ce qu'on connaît : les coordonnées des deux points rouges (pointeur et coin bas droit).
Ce qu'on sait : les deux lignes continues bleues sont de même taille.





Depuis les points rouges, on peut tracer un rectangle dont on connaît hauteur et largeur (du moins pourra-t-on les calculer).







Du coup en coupant ce rectangle selon la diagonale on obtient deux triangles rectangle, et ça c'est pratique : puisqu'on connaît hauteur et largeur, on saurait en calculer les angles, si besoin était.






Considérons donc qu'on connait une “portion” de l'angle cherché : l'angle du haut, noté ang







Reste à trouver l'angle marqué en noir : l'angle vert (qu'on cherche) vaudra angle rouge (ang) plus angle noir.







Et c'est là qu'on se souvient que les deux lignes marquées en bleu sont de même taille et que par conséquent on a un triangle isocèle





Et que c'est rudement bien les triangles isocèle parce que les angles à la base sont égaux.







Ayé vous voyez la ruse ?
Je vous rappelle que…


… les angles opposés sont égaux…

… et qu'on connait ang







Et oui… les angles rouges et noirs sont les mêmes
Vu qu'on sait que :
angle vert = angle rouge + angle noir
On en conclu, ébahis, que :
angle cherché = 2 * ang

Pour obtenir l'inclinaison du clip _verso, il suffit donc de “trouver” ang, qui appartient à un triangle rectangle dont on connaît deux côtés… Trop facile !

Le code



Traduire en code, puisque la classe Math nous fournit tout le petit matériel nécessaire, ça va être un jeu d'enfants.

Tout d'abord on a besoin des coordonnées du coin de départ dans contenant, on va les stocker dans une variable globale (plus tard quand on s'occupera des autres coins forcément ça changera).
Déclarons donc une variable _pCoinDep, de type Point (pour stocker les coordonnées d'un point, on n'a pas trouvé mieux ;))

\\
var _pCoinDep:Point=new Point(_larg,0);\\



Puis dans la fonction il reste à calculer l'angle qui nous intéresse (noté ang en rouge sur le croquis) :

\\
function placePage(e:Event=null):void {\\
[]\\
	// Angle de base -------------------------\\
// le côté adjacent, la largeur sur le croquis\\
	var adj:Number=_pCoinDep.x-x_c;\\
// le côté opposé, la hauteur sur le croquis\\
	var opp:Number=_pCoinDep.y-y_c;\\
// on sait que tangente = opposé sur adjacent\\
	var tang:Number=opp/adj;\\
// Math.atan nous renvoie - en radian - la mesure d'un angle dont on connaît la tangente\\
	var angR:Number=Math.atan(tang);// radian\\
// penser à convertir en degré pour la prorité rotation ;)\\
	var angD:Number=angR*180/Math.PI;// degres\\
\\
	_verso.x=x_c;//  suit point courant\\
	_verso.y=y_c;\\
// --> ici on applique la rotation\\
	_verso.rotation=angD*2;//  inclinaison de la page\\
\\
[]\\



Pas plus, et le clip verso s'incline selon la position de la souris



Là je ferais bien une pause, mais Ronchon est trop enthousiaste, on ne le tient plus, il veut placer les masques pour achever l'illusion. Il se souvient que le masque s'appuie sur la médiatrice de notre fameux triangle isocèle 5) et affirme qu'on a tout ce qu'il faut pour finir, que c'est l'affaire de 5 minutes…


Placer les masques



Il va donc s'agir déplacer un rectangle (un vrai, un objet Shape) sur la ligne grise, celle que j'ai nommée ligne de pliure en début de chapitre.

Voici ce que vous attendez, j'ai ajouté les lignes de construction et une bascule pour masquer ou non le verso, ne vous en préoccupez pas.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Ci-dessus c'est juste pour voir ce qu'il se passe : les dimensions du masque sont arbitraires (et ne conviennent pas), ne vous compliquez pas maintenant, faites pareil.

Fabriquer les masques


Puisqu'il nous faudra deux formes rectangulaires (une pour le verso, une autre pour le recto, dans l'élan et tant qu'à faire), je vous laisse vous employer à les fabriquer, sur le calque Déclare-Init.

- Et vu que vous n'avez besoin de personne je file me chercher un café, zutalafin. Que les plus impatients ne se privent pas de s'essayer à aller jusqu'au bout : orientation et déplacement du masque, vous disposez de tous les indices nécessaires -


\\
var taillopif:Number=200;\\
var taillopif2:Number=100;\\
var _mVerso:Shape= fabRectHM(taillopif, taillopif2);\\
var _mRecto:Shape= fabRectHM(taillopif, taillopif2);\\
\\
contenant.addChild(_mVerso);\\
// ligne en commentaire le temps de voir ce qui se passe\\
//_verso.mask=_mVerso;\\
\\
\\
function fabRectHM(l:Number,h:Number):Shape {\\
	var s:Shape=new Shape;\\
	s.graphics.beginFill(0x333333);\\
	s.graphics.drawRect(-l/2,0,l,h);\\
	return s;\\
}\\



Les dimensions du rectangle sont donc choisies au pifomètre, et vous avez pris soin de disposer le point d'origine (0/0) en haut au milieu.


Calculer l'angle de rotation



Pour ce qui est de l'angle appliqué au masque, c'est dans la fonction placePage que ça se passe (puisque c'est recalculé sans cesse selon la position de la souris)

La ligne de pliure c'est tout bonnement la médiatrice du triangle isocèle qu'on a isolé un peu plus haut (représentée en noir sur le croquis ci-dessous).

Souvenez vous : la fin de la vidéo d'intro démontre, par pliage, que la ligne de pliure détermine un axe de symétrie sur ce fameux triangle isocèle (segments S et S' et base rouge). Dans un triangle isocèle, la ligne qui passe par son sommet et le milieu de sa base, c'est la médiatrice. Elle a pour autre caractéristique d'être perpendiculaire à la base.








Calculer l'angle de cette médiatrice, c'est tout bête puisqu'on sait qu'elle est perpendiculaire à la ligne rouge dont on connaît l'angle (on vient de le calculer)

\\
	var angMedD:Number=angD+90;// elle est perpendiculaire\\
	////                                        c'est la ligne de masque\\


Voici donc pour la propriété rotation du masque _mVerso.

Les coordonnées



Reste à trouver ses coordonnées.
Je propose de s'appuyer sur un point facilement calculable : l'intersection entre la médiatrice et la base (rouge) du triangle, puisqu'on sait que le propre de la médiatrice c'est de passer par le milieu de la base.

\\
	var pM:Point= new Point(); // le point du milieu\\
\\
	pM.x = (x_c + _pCoinDep.x) / 2;\\
	pM.y = (y_c + _pCoinDep.y) / 2;\\
// coorconnées\\
	_mVerso.x=pM.x;\\
	_mVerso.y=pM.y;\\
// rotation\\
	_mVerso.rotation=angMedD;\\



Jusque là, ça va…

Reste à faire la même chose avec _mRecto et à faire propre : ne pas voir les masques à l'initialisation, et se débrouiller pour qu'ils ne remplissent leur office que quand ça nous intéresse. Quant aux dimensions des masques, je suis restée à l'étape 'taille au pif' et constaté très empiriquement que les valeurs ci-dessous convenaient tout à fait…

\\
var taillopif:Number=_haut*2.5;\\
var taillopif2:Number=_haut*1.5;\\




Le code complet



• Calque déclare init

\\
var _pCoinDep:Point=new Point(_larg,0);\\
\\
var contenant:MovieClip= new MovieClip();\\
var _recto:P1= new P1();\\
var _verso:P2=new P2();\\
\\
contenant.addChild(_recto);\\
contenant.addChild(_verso);// doit être au dessus de recto\\
contenant.x= 150;\\
contenant.y= 350;\\
addChild(contenant);\\
\\
var _haut:Number=_recto.height;\\
var _larg:Number=_recto.width;\\
\\
_verso.x=_larg;// à droite de recto\\
_verso.visible=false;\\
\\
var taillopif:Number=_haut*2.5;\\
var taillopif2:Number=_haut*1.5;\\
\\
var _mVerso:Shape= fabRectHM(taillopif, taillopif2);\\
var _mRecto:Shape= fabRectHM(taillopif, taillopif2);\\
\\
contenant.addChild(_mVerso);\\
_mVerso.visible=false;\\
contenant.addChild(_mRecto);\\
_mRecto.visible=false;\\



• La fonction

function fabRectHM(l:Number,h:Number):Shape {
	var s:Shape=new Shape;
	s.graphics.beginFill(0x333333);
	s.graphics.drawRect(-l/2,0,l,h);
	return s;
}


• Calque code

contenant.addEventListener(MouseEvent.MOUSE_DOWN,demarre);
stage.addEventListener(MouseEvent.MOUSE_UP,arrete);
 
var _tm:Timer=new Timer(50);
_tm.addEventListener(TimerEvent.TIMER,placePage);
 
function demarre(me:MouseEvent):void {
	_tm.start();
}
 
function arrete(me:MouseEvent) {
	_verso.visible=false;
	_recto.mask=null;
	_tm.stop();
}
 
 
function placePage(e:Event=null):void {
	_recto.mask=_mRecto;
	_verso.mask=_mVerso;
	_verso.visible=true;
 
	var pM:Point= new Point();// point au milieu ligne coinDep-pointeur
 
	var x_c:Number=contenant.mouseX;
	var y_c:Number=contenant.mouseY;
 
	// Angle de base -------------------------\\
	var adj:Number=_pCoinDep.x-x_c;
	var opp:Number=_pCoinDep.y-y_c;
	var tang:Number=opp/adj;\\
	var angR:Number=Math.atan(tang);// radian
	var angD:Number=angR*180/Math.PI;// degres
	\\
	////// place point milieu --------------------------------------------------\\
	pM.x = (x_c + _pCoinDep.x) / 2;
	pM.y = (y_c + _pCoinDep.y) / 2;
	var angMedD:Number=angD+90;// elle est perpendiculaire\\
\\
	////// Bouge visuels -------------------------------------------------------\\
\\
	////// les masques\\
	_mVerso.x=pM.x;
	_mVerso.y=pM.y;
	_mVerso.rotation=angMedD;
\\
	_mRecto.x=pM.x;
	_mRecto.y=pM.y;
	_mRecto.rotation=angMedD;
\\
	////// la page\\
	_verso.x=x_c;//  suit point courant\\
	_verso.y=y_c;
	_verso.rotation=angD*2;//  inclinaison de la page\\
	}




Ajouter les lignes de construction pour la suite



Pour le plaisir de bien voir ce qui se passe, et parce que ça va nous aider pour la suite, je vous propose d'ajouter les lignes de construction et une pastille pour figurer le point d'intersection entre médiatrice et base de l'isocèle (aux coordonnées de pM donc).

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.
La bascule pour afficher ou non les lignes de construction 'est du bonus pour le tuto, les sources ne s'en encombrent pas ;)

On pourrait dessiner les lignes dynamiquement, et ne vous en privez pas si vous préférez, moi je me contenterai d'un clip avec une ligne





• Calque Declare init :

\\
// = = = = = = Dessin = = = = = = = = = = = = = \\
// si vous êtes daltoniens\\
var cDiagBase:Number=0xff0000;// diagonale de base (coinDep/souris)\\
var cMed:Number=0x707070;// point milieu et médiatrice\\
var cPage:Number=0x0000ff;// orientation de la page\\
\\
var scoin_c:Shape= new Shape();// coin courant (suit souris)\\
scoin_c.graphics.beginFill(cDiagBase);\\
scoin_c.graphics.drawCircle(0,0,5);\\
contenant.addChild(scoin_c);\\
\\
var sM:Shape= new Shape();// milieu de "diagonale de base"\\
sM.graphics.beginFill(cMed);\\
sM.graphics.drawCircle(0,0,5);\\
contenant.addChild(sM);\\
\\
// les lignes de construction\\
var ligneCoinSouris:Mv_Ligne=new Mv_Ligne();\\
var coulDiag:ColorTransform= new ColorTransform();\\
coulDiag.color=cDiagBase;\\
ligneCoinSouris.transform.colorTransform=coulDiag;\\
\\
contenant.addChild(ligneCoinSouris);\\
\\
var ligneMed:Mv_Ligne=new Mv_Ligne();//Mediatrice\\
var coulMed:ColorTransform= new ColorTransform();\\
coulMed.color=cMed;\\
ligneMed.transform.colorTransform=coulMed;\\
\\
contenant.addChild(ligneMed);\\
\\
var lignePage:Mv_Ligne=new Mv_Ligne();// inclinaison page\\
var coulPage:ColorTransform= new ColorTransform();\\
coulPage.color=cPage;\\
lignePage.transform.colorTransform=coulPage;\\
\\
contenant.addChild(lignePage);\\



Calque code, fonction placePage

\\
function placePage(e:Event=null):void {\\
[]\\
	// dessin - - - - - - - - - - - - - - - - - -\\
	scoin_c.x=x_c;// suit point courant\\
	scoin_c.y=y_c;\\
	\\
	ligneCoinSouris.x=x_c;// suit point courant\\
	ligneCoinSouris.y=y_c;\\
	ligneCoinSouris.rotation=angD;\\
	\\
	lignePage.x=x_c;// suit point courant\\
	lignePage.y=y_c;\\
	lignePage.rotation=_verso.rotation;\\
	\\
	\\
	sM.x=pM.x ;// suit point milieu\\
	sM.y=pM.y;\\
	\\
	ligneMed.x=pM.x ;// suit point milieu\\
	ligneMed.y=pM.y;\\
	ligneMed.rotation=angMedD;// inclinaison médiatrice\\
\\
}\\



Nous voilà mûrs pur la suite, jusque là ça marche pour le coin en bas à droite, s'agirait que ça fonctionne aussi pour les trois autres.

Les quatre coins


L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Le coin de départ

On veut donc que la fonction placePage remplisse son office quelque soit le coin de départ de référence. Dit autrement, il s'agit que ça marche aussi quand _coinDep vaut 0/0 (bas/gauche) ou 0/-_haut (haut/gauche), ou _larg/-_haut (haut/droit).

Première chose à faire : valoriser _coinDep avant de lancer placePage. Je propose de le faire dans la fonction demarre
Ici on se contentera de “couper la page en quatre”, et de valoriser _coinDep selon la position du pointeur (quart bas/droit, haut/droit, etc.).
Ce qui nous donne :

function demarre(me:MouseEvent):void {
 
	if (me.localY < -_haut/2 && me.localX>_larg/2 ) {
		trace("haut droit");
		_pCoinDep.x=_larg;
		_pCoinDep.y=-_haut;
 
	} else if (me.localY < -_haut/2 && me.localX<=_larg/2) {
		trace("haut gauche");
		_pCoinDep.x=0;
		_pCoinDep.y=-_haut;
 
	} else if (me.localY >= -_haut/2 && me.localX<=_larg/2) {
		trace("bas gauche");
		_pCoinDep.x=0;
		_pCoinDep.y=0;
 
	} else {
		trace("bas droite");
		_pCoinDep.x=_larg;
		_pCoinDep.y=0;
	}
	//
	_tm.start();
}


Tel que, ça fonctionne toujours si on clique en bas à droite (ouf !), déjà moins bien si on clique en haut à droite, et carrément plus du tout pour les deux autres coins.

On s'en doutait un peu, il faut bien le dire ;)

La position du visuel


Regardons ce qui fonctionne presque : le coin haut droit…
En fait c'est quasi ça, si ce n'est que ce n'est pas le bon coin de _verso qui suit le pointeur.

Hé oui : rappelons nous, les coordonnées du clip visuel dans P2 (symbole de bibliothèque) sont à 0/0. Quand P2 suit le pointeur, c'est aussi le coin bas gauche du clip inclus (a) qui suit le mouvement. Or, quand on change le coin saisi au départ sur le recto (celui qu'on saisit dans la vraie vie) on change forcément aussi le coin saisi au verso :idea:
Pour ceux qui sont ausi handicapés que je le suis des facultés de visualisation dans l'espace, j'ai illustré les correspondances :



Si ce n'est que ça ! :D
Aussitôt vu, aussitôt fait : yaka déplacer le clip contenu dans _verso afin que le coin situé en 0/0 de P2 soit celui qui lui est “associé” sur le recto :

Et c'est là que les intrépides qui ont choisi de ne pas faire du visuel de P2 un clip inclus en mesurent les conséquences, et n'ont plus qu'à s'y mettre ;) 6)


function demarre(me:MouseEvent):void {
        // récupérer le clip inclus
	var visuel:DisplayObject=_verso.getChildAt(0);
	if (me.localY < -_haut/2 && me.localX>_larg/2 ) {
		trace("haut droit ");
		visuel.x=0;
		visuel.y=_haut;
		_pCoinDep.x=_larg;
		_pCoinDep.y=-_haut;
	} else if (me.localY < -_haut/2 && me.localX<=_larg/2) {
		trace("haut gauche");
		visuel.x=-_larg;
		visuel.y=_haut;
		_pCoinDep.x=0;
		_pCoinDep.y=-_haut;
	} else if (me.localY >= -_haut/2 && me.localX<=_larg/2) {
		trace("bas gauche");
		visuel.x=-_larg;
		visuel.y=0;
		_pCoinDep.x=0;
		_pCoinDep.y=0;
 
	} else {
		trace("bas droite 1");
		visuel.x=0;
		visuel.y=0;
		_pCoinDep.x=_larg;
		_pCoinDep.y=0;
	}
	//
	_tm.start();
}

Ça y est ça fonctionne pour les deux coins de droite.
A gauche… C'est pas loin…

Les masques en "symétrie"


Si, si Ronchon, ne désespère pas on y est presque, regarde bien, affiche un masque temporairement pour y voir plus clair… Ça y est tu repères le hic ? Vous aussi, vous avez vu ?
C'est le masque qui n'est pas du bon côté de la ligne de pliure !

Il faut qu'il pivote de 180° pour se retrouver du bon côté (un demi tour quoi).

Convenons donc d'une variable globale _sym (comme symétrie, même si c'est un abus de langage) qui vaudra 0 quand on saisit à droite et 180 quand on saisit à gauche. On la valorise dans demarre (dans chacune des quatre branches) ; Dans placePage on l'ajoute à la rotation des masques… Et le tour est joué :)

 
function demarre(me:MouseEvent):void {
 
	var visuel:DisplayObject=_verso.getChildAt(0);
	if (me.localY < -_haut/2 && me.localX>_larg/2 ) {
		trace("haut droit");
		visuel.x=0;
		visuel.y=_haut;
		_pCoinDep.x=_larg;
		_pCoinDep.y=-_haut;
// ICI ----> à droite rien à changer, la "symetrie" vaut 0
		_sym=0;
	} else if (me.localY < -_haut/2 && me.localX<=_larg/2) {
		trace("haut gauche");
		visuel.x=-_larg;
		visuel.y=_haut;
		_pCoinDep.x=0;
		_pCoinDep.y=-_haut;
// ICI ----> à gauche il faudra pivoter les masques, la "symetrie" vaut 180
		_sym=180;
 
[]
function placePage(e:Event=null):void {
	[]
	////// les masques
	_mVerso.x=pM.x;
	_mVerso.y=pM.y;
	_mVerso.rotation=angMedD+_sym;
 
	_mRecto.x=pM.x;
	_mRecto.y=pM.y;
	_mRecto.rotation=angMedD+_sym;
	//



Pour que ce soit tout propre quand on relâche la souris, un coup de plumeau dans arrete :

function arrete(me:MouseEvent) {
	_verso.visible=false;
	_recto.mask=null;
	_tm.stop();
}



A ce stade le principe est isolé et la fonction de base, on la tient.
Reste à identifier les points de déchirure afin que le mouvement de la feuille soit limité (au besoin) par la ligne de reliure (verticale opposée au coin de saisie)

Identifier les points de déchirure

Quand on tourne des pages reliées entre elles, le mouvement est limité par la reliure, justement.

Observez ce qui se produit en l'état en vous concentrant sur la ligne de pliure (la médiatrice grise). Le point de déchirure est atteint quand l'intersection entre cette ligne et le bord haut - ou bas - de la page est au-delà de la reliure (ou en deçà, selon le coin de départ choisi). Observons ce qui se passe quand on corne depuis le coin en bas à droite (pour ne pas changer des habitudes).

Deux cas de figure se présentent, je les commente sur l'animation ci-dessous :
L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Maintenant que le principe est compris, reste à traduire… un jeu d'enfant :)
Convenons d'une fonction dechire qui renverra vrai si un des points de déchirure est dépassé.
Elle aura besoin du point pM - auquel on se réfère sans cesse -
L'autre donnée qu'on considère acquise dans la démo ci-dessus c'est angMed.
La fonction placePage le calcule et l'exprime en degrés, il faudra penser à le convertir.

function dechire(pM:Point,a:Number):Boolean {
	//---- l'angle
	var angMed:Number=(a/180*Math.PI);
 
	// ---- opposé
	// haut ou bas ?
	var bord:Number = _pCoinDep.y;
	// voir animation tuto
	var opp:Number =pM.y<bord?pM.y:_haut+pM.y;
 
	// --- adjacent via tangente
	var adj:Number=opp/Math.tan(angMed);
 
	//trace("opp "+opp);
	//trace("adj "+adj);
	// positon x
	var posX:Number=pM.x-adj;
 
	// renvoie vrai si à l'extérieur des marges
	var bDéchire:Boolean;
	bDéchire=posX>_larg || posX<0;
	return bDéchire;
}



Pour vérifier, on peut tester dans placePage :

function placePage(e:Event=null):void {
	_recto.mask=_mRecto;
	_verso.mask=_mVerso;
	_verso.visible=true;
 
	var pM:Point= new Point();
 
	var x_c:Number=contenant.mouseX;
	var y_c:Number=contenant.mouseY;
	// Angle de base -------------------------
	var adj:Number=_pCoinDep.x-x_c;
	var opp:Number=_pCoinDep.y-y_c;
	var tang:Number=opp/adj;
	var angR:Number=Math.atan(tang);// radian
	var angD:Number=angR*180/Math.PI;// degres
 
	// place point milieu --------------------------------------------------
	pM.x = (x_c + _pCoinDep.x) / 2;
	pM.y = (y_c + _pCoinDep.y) / 2;
	var angMedD:Number=angD+90;// elle est perpendiculaire
	////                                        c'est la ligne de masque
 
// -- > ICI 	
	if (dechire(pM,angMedD)) {
		return;
	}
 
	////// Bouge visuels ----------------------------------------------------------------------
	[.]
}


pas encore rédigé illustré… ça va viendre

Plus fluide

blabla

Un effet d'inertie sur le point courant

• Calque Declare Init

var x_c:Number;
var y_c:Number;


• Calque code,

Fonction demarre

	x_c =_pCoinDep.x;// suit pointeur, donc départ coin concerné
	y_c =_pCoinDep.y;



Fonction placePage

x_c += (contenant.mouseX - x_c) / 10;
y_c += (contenant.mouseY - y_c) / 10;

Conserver le mouvement quand on dépasse les points de déchirure

méthode A

	var pFace:Point= new Point(coinFaceX,_pCoinDep.y);
	var pPointeur:Point=new Point(x_c,y_c);
	var C:Point= pPointeur.subtract(pFace);
 
	if ( C.length>_larg) {
		C.normalize(_larg);
		pPointeur = C.add(pFace);
		x_c=pPointeur.x;
		y_c=pPointeur.y;
	}


méthode B

        var adj:Number=x_c-coinFaceX;
	var opp:Number=y_c-_pCoinDep.y;
	var hyp:Number=Math.sqrt(opp*opp+adj*adj);
 
	if (hyp>_larg) {		
		var angOppR:Number=Math.atan2(y_c-_pCoinDep.y,x_c-coinFaceX);
		var distX:Number=Math.cos(angOppR)*_larg;
		var distY:Number=Math.sin(angOppR)*_larg;
 
		x_c=coinFaceX+distX;
		y_c=_pCoinDep.y+distY;				
	}



function placePage(e:Event=null):void {
	_recto.mask=_mRecto;
	_verso.mask=_mVerso;
	_verso.visible=true;
	var pM:Point= new Point();
	//
	x_c += (contenant.mouseX - x_c) / 10;
	y_c += (contenant.mouseY - y_c) / 10;
 
// = = =>> ICI
	// distance pointeur coin de reliure sup à largeur 
        // --> disposer x et y courant sur cercle de rayon _larg
 
        // le centre du cercle c'est le coin face à pCoinDep
	var coinFaceX:Number=_pCoinDep.x==0?_larg:0;
        // calcul de la taille de l'hypothénuse : distance pointeur coin de reliure
	var adj:Number=x_c-coinFaceX;
	var opp:Number=y_c-_pCoinDep.y;
	var hyp:Number=Math.sqrt(opp*opp+adj*adj);
 
	if (hyp>_larg) {
		// angle du segment 'pointeur / coin de déchirure'
		var angOppR:Number=Math.atan2(y_c-_pCoinDep.y,x_c-coinFaceX);
		var angOpp:Number=angOppR*180/Math.PI;
		var distX:Number=Math.cos(angOppR)*_larg;
		var distY:Number=Math.sin(angOppR)*_larg;
                // placer le point courant à la circonférence du cercle
		x_c=coinFaceX+distX;
		y_c=_pCoinDep.y+distY;		
 
		// pour voir ce qu'il se passe
		ligneMed.rotation=angOpp;// inclinaison médiatrice\\
		ligneMed.x=coinFaceX;
		ligneMed.y=_pCoinDep.y;
 
	}
	// pour voir ce qu'il se passe
	scoin_c.x=x_c;// suit point courant\\
	scoin_c.y=y_c;
 
 
// ensuite rien ne change, on reprend les calculs depuis le point courant éventuellement recalculé
 
	adj=_pCoinDep.x-x_c;
	opp=_pCoinDep.y-y_c;
 
	var tang:Number=opp/adj;
	var angR:Number=Math.atan(tang);// radian
	var angD:Number=angR*180/Math.PI;// degres
 
	////// place point milieu --------------------------------------------------
	pM.x = (x_c + _pCoinDep.x) / 2;
	pM.y = (y_c + _pCoinDep.y) / 2;
 
	// vérifie déchirure pour l'autre cas
	if (dechire(pM,angR)) {
		return;
	}
	var angMedD:Number=angD+90;// elle est perpendiculaire
	////                                        c'est la ligne de masque
 
	////// Bouge visuels ----------------------------------------------------------------------
	////// les masques
	_mVerso.x=pM.x;
	_mVerso.y=pM.y;
	_mVerso.rotation=angMedD+_sym;
 
	//_mVerso.rotation=angMedD+_sym;
	_mRecto.x=pM.x;
	_mRecto.y=pM.y;
	_mRecto.rotation=angMedD+_sym;
	////
	//
	////// la page
	_verso.x=x_c;//  suit point courant
	_verso.y=y_c;
	_verso.rotation=angD*2;//  inclinaison de la page
	//}
}

http://www.flp-sp.com.br/php/communautaire/spip.php?rubrique41&lang=fr

1) par ordre alpha : je me refuse à établir une quelconque hiérarchie dans l'aide apportée :D
2) a : pour angle
3) la médiatrice d'un segment est l'ensemble des points équidistants des extrémités du segment
4) une habitude d'écriture très perso veut que _c signifie “courant”
5) la ligne de pliure dans la vidéo, à ce moment là je parle de cacher ce qui déborde aussi bien de recto que de verso (je mets quand même pas la référence à la minute ? dis Lilive ?:mrgreen:
6) Afin de ne pas polluer la progression du tuto je ne développerai pas le sujet ici, si certains d'entre vous ont des questions j'y répondrai avec plaisir dans la discussion associée