Dessiner courbe au fur et à mesure
Ceetix
Membre
Sur les conseil d'AliGator j'ouvre un nouveau post sur une technique d'animation que j'aimerai tester.
Pour ma petit application de graphe je trace mes arc en courbe d'un seul coup ainsi :
J'aimerai bien animer le tracer, que mon arc soit tracé petit à petit.
Une ou plusieurs méthodes? :P
Pour ma petit application de graphe je trace mes arc en courbe d'un seul coup ainsi :
<br />-(void)drawArc:(Arc *)a<br />{<br /> NSBezierPath *tracer = [NSBezierPath bezierPath];<br /> [tracer moveToPoint:(NSMakePoint(a.d.abs + a.d.diametre/2,a.d.ord+ a.d.diametre/2))];<br /> [tracer curveToPoint:(NSMakePoint(a.a.abs+ a.a.diametre/2,a.a.ord+ a.a.diametre/2)) <br /> controlPoint1:(NSMakePoint(a.d.abs,a.d.abs)) <br /> controlPoint2:(NSMakePoint(a.a.abs,a.a.ord))];<br /> [a.couleur set];<br /> [tracer stroke];<br /> <br /> [self setNeedsDisplay:YES];<br />}<br />
J'aimerai bien animer le tracer, que mon arc soit tracé petit à petit.
Une ou plusieurs méthodes? :P
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
(1) Bon je reviendrais pas sur ta façon de nommer tes variables avec des noms un peu trop courts, ou d'utiliser abs et ord pour positionner tes sommets au lieu d'utiliser un NSPoint (que j'aurais utilisé d'ailleurs pour indiquer la position du centre de ton sommet et non son coin supérieur gauche)... ça me démange, mais bon :P
(2) dans tes controlPoints, je pense que tu as fait une faute de frappe en créant ton point en (a.d.abs,a.d.abs), j'imagine qu'en 2e coordonnée tu voulais mettre a.d.ord plutôt ?
(3) Tu coup si je ne me trompe pas, comme les controlPoints sont tous les deux colinéaires à ton point de départ et ton point d'arrivée... au final ta courbe de Bézier va se résumer... à une bête ligne ! Donc autant appeller addLineToPoint plutôt que addCurveToPoint en lui donner des controlPoints pour arriver à ce résultat !
Ah quoique j'ai un doute, ton controlPoint2 étant "avant" le point d'arrivée, ça va sans doute faire une petit boucle sur l'arrivée.. enfin bon faudra sûrement ajuster tes controlPoints pour faire ce que tu veux quand même... ou si tu veux une ligne, te contenter de addLineToPoint
(4) C'est quoi ce setNeedsDisplay dans une méthode de draw ?! drawArc est appelé par drawRect j'imagine, qui boucle sur tous tes arcs et appelle la méthode drawArc en passant l'arc à dessiner, non ?
Donc il n'a rien à faire dans une méthode de draw ! setNeedsDisplay est à appeler quand on modifie notre modèle (par exemple quand tu ajoutes un sommet dans ton tableau de sommets ou un arc dans ton tableau d'arcs) pour dire à Cocoa "hé j'ai modifié un truc dans mon modèle, va recalculer le dessin à afficher pour dessiner tout ce bouzin".
Si on a demandé un setNeedsDisplay à une vue quand on a changé notre modèle, ça aura pour effet que Cocoa va appeler la méthode drawRect sur cette vue quand il en sera à dessiner ton interface à l'écran pour recalculer la façon de dessiner cette vue... et mémoriser cette façon de dessiner dans sa mémoire pour pas avoir à refaire les calculs à chaque fois... sauf si tu lui demandes de les refaire avec setNeedsDisplay...
---
Une fois que tout ceci sera mis au clair, cela dépend ensuite de l'animation que tu veux.
Pour ce qui est de l'animation, si je suppose que ton BezierPath est en fait une ligne, semble-t-il... les premières solutions qui me viennent à l'esprit grossièrement, c'est d'utiliser une animation de type "balayage" ("wipe").
Cela va révéler ton nouveau dessin progressivement par exemple de la gauche vers la droite (ou du haut vers le bas, ou...) : au début tu as ton "ancienne version" sans le bezier path, et au fur et à mesure la partie gauche de ta vue affiche la nouvelle version (tandis que sur la partie droite c'est encore l'ancienne version)... jusqu'à ce que toute ta vue affiche la "nouvelle version" de ton dessin avec le bezierpath dessiné.
C'est une solution des plus simple car en plus elle utilise des effets CoreAnimation dédiés. Tu peux déjà commencer par ça, j'ai pas le temps ce soir pour te décortiquer tout ça, mais en gros je pense qu'il faut utiliser pour ce genre d'effet une CATransition, avec le type "kCATransitionReveal" et le subtype indiquant le sens de ta transition (typiquement kCATransitionFromLeft par exemple).
Ensuite tu installes ton animation (méthode [tt]addAnimation: forKey:[/tt]) sur le CALayer de ta vue (il faut avoir coché la vue pour laquelle tu veux animer le layer dans la partie "Wants Core Animation Layer" de l'Effects Inspector dans IB, sinon elle n'aura pas de layer CoreAnimation... ou alors faut le créer par code mais bon) au moment où tu veux faire jouer ta transition.
Déjà essaye ça, ça sera un bon début : créer ta CATransition, et ajouter cette animation au layer de ta vue quand tu veux la déclancher typiquement juste à la fin du code où tu as ajouté ton arc... (je parle de la méthode qui ajoute l'arc dans le modèle, donc typiquement qui fait un addObject d'un Arc* dans ton "tableauArcs" puis appelle setNeedsDisplay juste après pour demander un recalcul du dessin... bah faut rajouter l'animation dans cette méthode, et tant qu'à rester logique avant le setNeedsDisplay.)
----
Bon cette méthode a l'avantage d'être un bon début pour commencer CoreAnimation, mais ce n'est pas une réelle animation du dessin de l'arc, dans le sens où la transition "reveal" va de la gauche vers la droite, elle ne suit pas le bezierpath que tu vas dessiner par ton arc... donc ça donnera quand même grosso modo l'effet voulu... si ton path ne revient pas sur ses pas avec une boucle (mais si c'est une ligne c'est bon).
Mais bon on pourra revenir sur ces petits détails après, commençons soft.
C'est bien... Maà®trise des bases avant de se prendre la tête sur des trucs user-friendly inutiles et compliqués :P
Des p'tits coups de dérivées, d'interpolations... Tu vois, les maths c'est important pour faire des trucs inutiles parfois
Sinon tu parles d'interpolation c'est comme dans flash donc? On calcule le nombre de point necéssaire au tracé etc ...
Après si tu veux vraiment animer le tracer du BezierPath plus tard, oui là va falloir mettre la main à la pâte côté maths. Car je ne crois pas qu'il y ait d'animation existante pour faire ça... Donc il va falloir que tu animes le point d'arrivée de ton Path dans ce cas, je ne vois pas d'autre solution dans l'immédiat...
... et pour que ça fasse l'effet voulu, il va falloir connaà®tre les coordonnées de chaque point de ta courbe de bézier finale (et pas juste laisser Cocoa la tracer tout seul quoi)... mais aussi sa dérivée en tout point, en fonction de tes points de contrôle choisis au point de départ et d'arrivée, pour pouvoir calculer les points de contrôle intermédiaire à utiliser lors de ton animation.
Et ça c'est déjà moins de la tarte...
En fait tu as beaucoup de chance... J'y ai réfléchi un peu en dormant, et ce sont des maths à peine niveau lycée :P (système d'équations à 4 inconnues, mais déjà linéarisé !)
En gros, on cherche les point Q0, Q1, Q2, Q3 tels que :
P0*(1-x*u)^3+3*P1*(x*u)*(1-x*u)^2+3*P2*(x*u)^2*(1-x*u)+P3*(x*u)^3
=
Q0*(1-u)^3+3*Q1*u*(1-u)^2+3*Q2*u^2*(1-u)+Q3*u^3
Ce qui donne quand on assimile les degrés équivalents :
* Q0 = P0 (logique...)
* Q1 = x*(P1-P0)+Q0
* Q2 = x^2*(P2-2*P1+P0)+2*Q1-Q0
* Q3 = x^3*(P3-3*P2+3*P1-P0)+3*Q2-3*Q1+Q0
Tu as donc les 4 points de la nouvelle courbe de Bézier qui est la sous-courbe de la principale en x (en faisant varier x de 0 à 1)
Petit veinard !
Petite vérification...
Pour x = 0 :
Q0 = P0
Q1 = P0
Q2 = P0
Q2 = P0
Pourquoi pas...
Pour x = 1 :
Q0 = P0
Q1 = P1
Q2 = P2
Q3 = P3
Bien !
[Edit] Correction avec les facteurs 3 !
[Edit] Quel niouf... j'ai oublié les coefficients 3 dans les calculs pour la courbe paramétrique...
- u semble être l'abscisse curviligne du bezierPath [EDIT]ah non plutôt le vecteur unitaire[/EDIT]
- x est une variable que tu vas varier de 0 (tout début du bezierPath, rien de construit) à 1 (bezierPath complet), les valeurs intermédiaires te permettant de faire avancer dans le path.
Les points Q sont les points intermédiaires du bezierPath si on ne construit le bezierPath que de x%... donc en x=1 on veut Pi=Qi, en x=0 tout le monde au même endroit donc Q0=P0 et Q3=P0 aussi (départ et arrivée confondus)... et les Qi intermédiaires pour un x donné te permettent de construire ton bezierPath avec ces 4 controlPoints au fur et à mesure que x avance.
[EDIT] En fait plutôt que l'abscisse curviligne, u représente plutôt le vecteur unitaire (1,1). Les Pi et Qi étant des vecteurs (x,y) (... enfin des points de l'espace vectoriel quoi), tu retrouves dans les équations de schlum en fait une double-équation à chaque fois, qui donne à la fois les valeurs pour x et pour y, en raisonnant directement dans l'espace vectoriel |R².
4 clics sur la fenêtre...
* Point de départ
* Point d'arrivée
* Contrôle 1
* Contrôle 2
-> L'animation commence
Un 5e clic et tout s'efface pour revenir à la configuration au lancement
Désolé Monsieur Pierre Bézier
Au fait, tous les calculs ci-dessus tiennent parce qu'une courbe de Bezier est une courbe paramétrique polynomiale de degré 3 et qu'une partie de cette courbe en est forcément une aussi (composition avec une fonction linéaire...)
Lol faut pas déprimer pour un 's' :P
PS : vous aurez remarqué que c'est fait à la va vite, il y a des erreurs mémoire sur le NSTimer (il manque 1 retain et un reset à nil...)
Allez, pour la peine, je corrige, je rajoute la gestion de la barre d'espace pour prendre des points au pif, et je mets la nouvelle version !
Je sais cela, mais j'ai gardé l'habitude de faire un retain sur un NSTimer, considérant illogique qu'il soit retenu par la runloop... (et aussi qu'il retienne sa cible...)
ça ne coûte pas grand chose