|
Télécharger
le .dir correspondant à cette étape |
Dans cette troisième variante, nous allons découvrir d'autres fonctions d'image, ainsi que la manière de créer des acteurs à la volée. Pour la première étape, nous allons réaliser une animation voisine de ce que nous avions fait dans la première variante, à ceci près que cette-fois-ci, nous n'avons plus quatre images mais une seule. Cette image sera déformée à partir du point où la souris a cliqué, quel que soit ce point. |
Comment allons-nous procéder ?
En réalité, nous n'allons pas tout à fait travailler à partir d'une seule image. Néanmoins, nous n'aurons qu'un seul acteur de base, de référence. C'est à dire que dans la réalisation de l'animation, nous n'importerons qu'un seul acteur bitmap. Tout le reste sera réalisé par le code que nous allons écrire.
Le trilobite se
comporte comme s'il était coupé en quatre par deux droites : la
verticale et l'horizontale passant par le curseur de la souris au moment où
cette dernière est enfoncée.
Il n'est pas pensable de prévoir tous les découpages possibles
et de préparer un ensemble statique de tous les acteurs qui seraient
nécessaires pour tous les points de clic possibles. On peut calculer
aisément le nombre d'acteurs qu'il faudrait alors : notre image, dans
l'animation, fait 400 pixels de large par 305 pixels de haut, soit 122 000 points
possibles pour un clic. Chaque clic nécessitant quatre acteurs, il nous
faudrait alors disposer d'une bibliothèque de 488 000 acteurs ! Et du
temps pour les fabriquer !
La solution est donc de fabriquer les quatre acteurs nécessaires au moment où on en a besoin, aux dimensions qu'il faut, et avec le contenu convenable.
Cette création d'acteur, puis la création d'un contenu aux dimensions souhaitées, et enfin l'affectation de ce contenu aux acteurs se fait à l'aide des instructions que nous allons voir ci-dessous. Une fois les acteurs créés, il suffit de les affecter à quatre sprites en avant plan de manière qu'ils recouvrent l'acteur de référence qui, lui, est en arrière-plan.
Pour créer
des acteurs, nous utiliserons l'instruction new(), déjà
utilisée dans d'autres contextes, à laquelle nous passerons en
arguments le type d'acteur que nous souhaitons créer (bitmap, forme vectorielle,
champ texte, etc.), et l'emplacement dans la distribution auquel nous souhaitons
le créer. Attention, si on créé un nouvel acteur dans un
emplacement où un acteur existe déjà, ce dernier est écrasé
au profit du nouvel acteur. Nous pourrons donc écrire :
new(#bitmap, member(15,1))
pour créer un acteur bitmap à la position 15 de la distribution
numéro 1.
Une fois ceci fait, l'acteur existe mais il est vide. Il ne contient rien.
Pour remplir cet acteur d'un contenu, nous allons faire appel à de nouvelles entités : les objets image.
A l'instar des
rect ou des point, les objets image peuvent exister en tant
que tel, ou être une propriété de la scène ou d'un
acteur bitmap. L'objet image contient la composition de pixels qui forment l'acteur
ou la scène.
Nous utiliserons donc ces objets image pour remplir les acteurs créés.
La manipulation
des objets image peut être de deux sortes :
- soit on les lit à partir d'un acteur donné ou on les affecte
à un acteur donné, et on écrira alors monImage
= member("monActeur").image ou member("monActeur").image
= telleImage. On capture l'image que montre la scène à
l'aide de l'instruction (the stage).image.
- soit on les créé en les déclarant tout simplement. Dans
ce dernier cas, ces objets image existent de manière autonome, et sont
susceptibles d'être manipulés. Mais ils ne peuvent être vus
que si on les affecte à une entité tangible, c'est à dire
à un acteur. A ce propos, attention : on peut affecter une image à
la propriété image d'un acteur ; on ne peut pas forcer l'image
de la scène en lui affectant un objet image. L'image de la scène
est en lecture seule.
Une fois créé, nous sommes ramenés au problème de
la création des acteurs, c'est à dire que ce sont des objets image
vides, qui ne possèdent qu'une dimension et une profondeur de couleur.
On déclare un objet image de la manière suivante, en stockant
dans une variable :
uneImage = image(lareur, hauteur, profondeurCouleur).
Pour attribuer à chacun des pixels de l'objet image une valeur de couleur,
l'ensemble formant l'image perceptible, il faut utiliser une méthode
fournie par le langage, laquelle méthode permet de recopier tout ou partie
des pixels d'un objet déjà existant dans un autre objet image,
vide ou non. Cette méthode s'appelle copyPixels(),
et nous allons voir tout de suite comment l'employer.
Il existe également une méthode qui permet de ne fixer qu'un seul
pixel (setPixel() ) et une autre qui permet à
l'inverse de lire la valeur d'un seul pixel (getPixel()
).
Nous verrons ces deux dernières méthodes en leur temps, et notamment
la méthode getPixel() lorsque nous utiliserons
une image vidéo provenant d'une caméra pour faire de la détection
de mouvement.
Comme on s'en doute
désormais, la méthode copyPixels() s'emploie avec des
arguments, lesquels indiquent :
- l'objet image source dans lequel aller chercher les pixels à recopier,
- le rect de destination dans l'objet image cible,
- le rect source dans l'objet image source.
Ces trois paramètres
appellent quelques précisions pour ne pas s'emmêler dans les différentes
coordonnées.
En effet, chaque
objet image comprend son propre système de coordonnées, similaire
au système général de l'animation. L'origine de ce système
est toujours le coin supérieur gauche de l'objet, les abscisses sont
toujours croissantes vers la droite, et les ordonnées sont toujours croissantes
vers le bas. Mais chaque objet image a son sytème.
Il faut donc bien faire attention dans les arguments de objetImageCible.copyPixels(objetImageSource,
rectDeDestination, rectSource) : le rect de destination
est exprimé par rapport au système de coordonnées de l'objet
image cible, tandis que le rect source est exprimé par rapport
au système de coordonnées de l'objet image source.
Le schéma ci-dessous explique ce qui précède :

Le rectangle qui a été copié (en orange sur le schéma) fait bien la même taille aussi bien du coté de la source que du coté de la destination, mais les coordonnées ne sont pas les mêmes parce que le système de coordonnées n'est pas le même, et que la position relative à l'intérieur de l'image totale n'est pas non plus la même.
Que se passe-t-il si les deux rect, source et destination, ne sont pas de taille identique ? Alors l'image contenue dans le rect source est redimmensionnée à la taille du rect de destination, soit dilatée, soit comprimée, avec les déformations qui vont avec. Ceci est illustré par cette animation.
Nous pouvons maintenant
écrire l'enchaînement du traitement qui va permettre la déformation
du trilobite :
quand on appuie sur la souris
mesure de la position de
la souris au moment de l'appui (pour connaitre les droites de découpage)
création d'un objet
image général (contenant la totalité de l'image)
création de quatre
objets image vides aux bonnes dimensions (un pour chaque quart délimité
par les droites verticale et horizontale passant par la souris)
remplissage de ces quatre
objets image avec une portion de l'image générale, selon le découpage
imposé par la position de la souris
création de quatre
acteurs bitmap
peuplement de ces quatre
acteurs avec les quatre objets image précédemment fabriqués
attribution de ces acteurs
à quatre sprites
positionnement de ces sprites
à l'endroit correct pour reconstituer l'image générale
...
Ensuite, pour la déformation, nous sommes ramenés très exactement aux manipulations que nous avons faites dans la première variante, puisque nous avons quatre sprites à déformer. A la translation près, puisque dans la première variante, il fallait tenir compte de l'écrat initial entre le point de clic de la souris et le point occupé par les quatre coins centraux des sprites, alors qu'ici, par construction, le point de déformation et la position de la souris sont identiques.
Les calculs géométriques que nous aurons à faire seront de déterminer les bonnes tailles des objets image et de placer les quatre sprites hôtes des quatre quarts au bon endroit, de manière à ce qu'ils soient adjacents d'une part, et que les bords de l'ensemble qu'ils forment coïncident avec les ord de l'image générale d'origine.
La position de la souris est connue, les limites de l'image générale à reconstituer sont connues également. Tout le reste ne sera qu'une suite d'additions et de soustractions, comme illustré sur le schéma ci-dessous :

Voici
donc quel sera le code :
on mouseDown
h
= the mouseH
v
= the mouseV
lImage
= member("trilobite").image
Img1
= image(h - sprite(5).left, v - sprite(5).top, 24)
Img2
= image(sprite(5).right - h, v - sprite(5).top, 24)
Img3
= image(h - sprite(5).left,sprite(5).bottom - v, 24)
Img4
= image(sprite(5).right - h, sprite(5).bottom - v, 24)
Img1.copyPixels(lImage,
img1.rect, rect(0,0,h - sprite(5).left,v - sprite(5).top))
Img2.copyPixels(lImage,
img2.rect, rect(h - sprite(5).left, 0, sprite(5).right - sprite(5).left, v -
sprite(5).top))
Img3.copyPixels(lImage,
img3.rect, rect(0, v - sprite(5).top, h - sprite(5).left, sprite(5).bottom-sprite(5).top))
Img4.copyPixels(lImage,
img4.rect, rect(h - sprite(5).left, v - sprite(5).top, sprite(5).right - sprite(5).left,
sprite(5).bottom - sprite(5).top))
repeat
with i = 7 to 10
![]()
new(#bitmap,
member(i, 1))
end
repeat
member(7,1).image
= Img1
member(8,1).image
= Img2
member(9,1).image
= Img3
member(10,1).image
= Img4
sprite(11).member
= member(7,1)
sprite(12).member
= member(8,1)
sprite(13).member
= member(9,1)
sprite(14).member
= member(10,1)
sprite(11).loc
= point(sprite(5).left + (member(7,1).width/2), sprite(5).top + (member(7,1).height/2))
sprite(12).loc
= point(h + (member(8,1).width / 2), sprite(5).top + (member(8,1).height /2))
sprite(13).loc
= point(sprite(5).left + (member(9,1).width /2), v + (member(9,1).height/2))
sprite(14).loc
= point(h + (member(10,1).width /2), v + (member(10,1).height /2))
updateStage
![]()
repeat
while the mouseDown
![]()
if
the mouseH > sprite(5).left + 10 and the mouseH < sprite(5).right -10
and the mouseV > sprite(5).top + 10 and the mouseV < sprite(5).bottom
- 10 then
![]()
![]()
leQuad1
= sprite(11).quad
![]()
![]()
leQuad2
= sprite(12).quad
![]()
![]()
leQuad3
= sprite(13).quad
![]()
![]()
leQuad4
= sprite(14).quad
![]()
![]()
leQuad1[3]
= the mouseLoc
![]()
![]()
leQuad2[4]
= the mouseLoc
![]()
![]()
leQuad3[2]
= the mouseLoc
![]()
![]()
leQuad4[1]
= the mouseLoc
![]()
![]()
sprite(11).quad
= leQuad1
![]()
![]()
sprite(12).quad
= leQuad2
![]()
![]()
sprite(13).quad
= leQuad3
![]()
![]()
sprite(14).quad
= leQuad4
![]()
![]()
updateStage
![]()
end
if
end repeat
end
A noter qu'il faut
inclure un updateStage à deux endroits dans cette partie du
script, une première fois pour que les quatre sprites apparaissent bien
au bon endroit et en affichant le bon acteur, une deuxième fois à
l'intérieur de la boucle de répétion de manière
à ce que la déformation soit visible en permanence tout le temps
qu'on maintient la souris en foncée et qu'on la déplace.
Le déplacement obtenu est schématisé ci-dessous :

Nous pouvons maintenant passer à l'étape suivante, dans laquelle nous allons nous occuper de ce qui se passe quand on lâche la souris...
| Retour à la troisième étape |