Rotation 3D des fenetres

RocouRocou Membre
06:29 modifié dans API AppKit #1
A défaut d'être un nouveau standard Apple, ça semble devenir une mode; les nouvelles versions de Pages et Numbers s'y sont mises: la rotation 3D des fenêtres. J'aimerais bien savoir si cet effet fait partie de Cocoa et si c'est facile à  mettre en oeuvre.
La "rotation 3D des fenêtres", c'est cet effet qui consiste à  faire tourner une fenêtre sur un axe vertical, à  l'instar d'une girouette qui tourne sur son axe. On passe ainsi du recto au verso d'une fenêtre. Je trouve cela très bien en terme d'interface.
Si quelqu'un sait mettre cela en oeuvre, je suis très intéressé. J'ai parcouru la doc et Internet sans rien trouver.
«1

Réponses

  • NoNo Membre
    06:29 modifié #2
    Avant 10.5, les "transitions" de fenêtre (ou même de l'écran entier) se faisaient via CoreGraphics (donc non cocoa, et pire, avec des fonctions partiellement undocumented).

    Depuis 10.5 et l'apparition de NSAnimation/NSViewAnimation, il se peut que cela soit possible de la faire sous cocoa, mais je n'ai pas testé.

    Si cela t'intéresse, je peux te donner un bout de code qui anime une fenêtre "en rotation 3D".
  • RocouRocou Membre
    06:29 modifié #3
    dans 1235727874:

    Si cela t'intéresse, je peux te donner un bout de code qui anime une fenêtre "en rotation 3D".

    Bien sûr que ça m'intéresse!  :o
  • NoNo Membre
    06:29 modifié #4
    Allons-y !

    L'effet de transition entre 2 fenêtres est réalisé par Quartz (le window-server de OS X).
    En fait cet effet ne se fait pas entre 2 fenêtres différentes, mais sur 1 seule fenêtre qui aura 2 aspects :
    - le premier juste avant la transition,
    - et le second juste après.

    La création d'une transition se fait en 2 étapes :
    - création de la transition,
    - lancement de la transition.

    C'est entre l'étape de création et l'étape de lancement qu'il faut redessiner la fenêtre dans son nouvel état (celui après transition). Tout ce qui sera redessiné ne se verra pas à  l'écran, car lors de la création, la fenêtre à  l'écran est remplacée par sa "photo".
    Les modifications faites sont stockées dans son backing-store.
    Le lancement de la transition se fera donc entre cette photo à  l'écran et la fenêtre telle qu'elle est dessinée dans son backing-store.

    Je t'ai fait un petit projet de quelques lignes pour illustrer ça.
    Le premier zip joint est l'appli de démonstration, et le second est le projet xcode.

    Le projet Xcode est 10.5 (utilisation .xib), mais en fait le code peut fonctionner sans modif depuis 10.2.
  • NseaProtectorNseaProtector Membre
    06:29 modifié #5
    Merci No, vraiment très bien , bien commenté, excellent !
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #6
    Pas mal pas mal,

    j'avais déjà  choppé ce genre de code.

    Mais ce que je voudrais faire moi, c'est ce qu'on voit dans Pages'09 : un flip de la fenêtre des choix de modèles vers le NSOpenPanel pour aller ouvrir un fichier. Je trouve ça terriblement classe et vu que mon appli est "Leopard Only', ce serait une bonne occas de joueur avec core animation.

    Un spécialiste CA dans le coin ??  :)

    J'avoue n'y connaitre vraiment rien...
  • NoNo Membre
    06:29 modifié #7
    dans 1235848873:

    Mais ce que je voudrais faire moi, c'est ce qu'on voit dans Pages'09 : un flip de la fenêtre des choix de modèles vers le NSOpenPanel pour aller ouvrir un fichier. Je trouve ça terriblement classe et vu que mon appli est "Leopard Only', ce serait une bonne occas de joueur avec core animation.


    Qu'appelles tu un "flip de la fenêtre" ?
    C'est quoi comme effet visuel (je n'ai pas Pages) ?
  • Nebuchad34Nebuchad34 Membre
    février 2009 modifié #8
    Lorsque l'utilisateur Clic sur "ouvrir un document existant" sur la fenêtre de sélection des modèles de pages, celle-ci se retourne 2 fois dans un bel effet 3D pour révéler le NSOpenPanel.

    voilà  une petite vidéo : http://cocoatreeapplications.com/_videos/FlippingInPages.mov
  • NoNo Membre
    06:29 modifié #9
    dans 1235850889:

    Lorsque l'utilisateur Clic sur "ouvrir un document existant" sur la fenêtre de sélection des modèles de pages, celle-ci se retourne 2 fois dans un bel effet 3D pour révéler le NSOpenPanel.

    voilà  une petite vidéo : http://cocoatreeapplications.com/_videos/FlippingInPages.mov


    Ah oui, c'est l'effet flip bien sûr...
    Dans le code du projet que j'ai joint, il suffit de changer la ligne
    config.type=7;
    par
    config.type=9;

    7 étant le code pour l'effet cube, et 9 pour celui du flip.

    D'autres effets sont disponibles : le 8 par exemple provoque un effet de tourbillon.
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #10
    Oui et non.

    L'effet d'apple est pas tout à  fait pareil, je pense qu'il utilise core animation. Mais la vrai question, c'est comment faire un flip sur le NSOpenPanel ?? D'autant plus que la taille de la fenêtre change contrairement à  tous le sexemple de flip de fenêtre que j'ai trouvé jusqu'ici, et qui utilise tous la combine des fonctions quartz non documentées.
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #11
    Le plus simple est d'utiliser CoreAnimation.
    D'autant que la plupart des propriétés des CALayer sont CAAnimatable, c'est à  dire qu'elles gèrent toutes seules l'animation.
    Un exemple simple, tu prends une NSView (créée par exemple dans IB, et sur laquelle tu as un IBOutlet). Si tu as activé CoreAnimation dessus (il faut cocher "Use Backing Store" ou un truc comme ça dans la palette d'informations IB c'est tout en haut de la palette dans une TableView qui liste les vues et sous-vues de la vue sélectionnée), tu pourras alors accéder à  myView.layer (qui est un CALayer si tu as activé le Has Backing Store, nil sinon, puisque par défaut les vues n'utilisent pas CoreAnimation inutilement)

    Du coup tu peux faire des trucs comme myView.layer.transform = CA3DTransformRotate(M_PI_2,0,1,0) ou qqch dans le genre pour effectuer un effet de rotation 3D de ta vue. Et ça suffit ! Ta vue va tourner autour de l'axe Y d'un angle de ∏/2, donc elle va commencer l'effet "flip". Il suffit alors de remplacer le contenu de myView par une autre NSView (celle que tu veux afficher à  la place de la courante), puis de refaire une rotation autour de Y de ∏/2 à  âˆ.


    Le problème c'est qu'autant pour les NSView ça marche très bien, on peut accéder à  la property layer qui est un CALayer et faire tout ça. Autant pour les NSWindow on a pas ça. Donc on ne peux pas le faire directement sur une NSWindow...
    Je pense que la solution est de créer, juste le temps de l'animation, une NSWindow borderless et à  fond transparent, et de lui mettre comme contentView le dessin de la fenêtre (il doit bien y avoir moyen de récupérer le buffer graphique de dessin de la fenêtre pour "dessiner la fenêtre" entière (contenu mais aussi barre de titre, etc, bref faire un snapshot de la fenêtre). Ensuite il suffit d'appliquer une CA3DTransform de ∏/2 à  contentView.layer.transform, de remplacer la contentView par une capture de l'autre fenêtre à  afficher, terminer la rotation ∏/2->∏, et une fois l'animation finie, on détruit tout ça (la NSWindow borderless et à  fond transparent et sa contentView quoi) et on met la vraie fenêtre finale à  la place.

    Avantage de cette méthode par rapport à  l'utilisation de CGSPrivate.h c'est qu'elle est officiellement documentée car utilise CoreAnimation. Reste à  savoir comment faire une "capture de la fenêtre" pour placer l'image de cette fenêtre en tant que ContentView. Je sais que c'est faisable puisque certains membres du forum l'ont fait quand ils ont programmé Fenêtres Volantes... mais je ne sais plus si la méthode utilisée était officielle ou undocumented, alors bon...

    Si quelqu'un à  une meilleure façon de procéder pour utiliser CoreAnimation pour faire cet effet, moi je n'ai réussi à  faire des trucs qu'avec les NSView pas les NSWindows...
  • Philippe49Philippe49 Membre
    06:29 modifié #12
    dans 1235873948:

    Avantage de cette méthode par rapport à  l'utilisation de CGSPrivate.h c'est qu'elle est officiellement documentée car utilise CoreAnimation. Reste à  savoir comment faire une "capture de la fenêtre" pour placer l'image de cette fenêtre en tant que ContentView.


    Voir l'exemple SonOfGrab pour utiliser la CGWindow API valable depuis 10.5
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #13
    Euh, ok, je vois l'idée mais pas simple quand même  :)

    Si je trouve la motivation un de ces 4 j'essaierai.

    Si jamais j'y arrive, je vous tiendrai au courant. Mais vu que ça fait 4 mois que je programme, faut pas pousser mémé dans les ortis...  ;)
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #14
    Pour ceusse que ça intéresse j'ai fait un petit projet d'essai.
    C'est pas encore parfait, mais ça marchotte :
    - Deux catégories dans NSWindow_Additions.h, la principale étant pour obtenir une NSImage à  partir d'une NSWindow (capture). (l'autre c'est juste pour positionner plus facilement une fenêtre d'après son centre)
    - Une classe WindowTransitionner qui permet de configurer puis effectuer une transition entre 2 fenêtres. Ca utilise le principe que j'ai décrit plus haut, à  savoir faire une capture des 2 fenêtres, créer une fenêtre transparente et borderless et mettre l'image de la capture dedans, et utiliser CoreAnimation pour faire l'effet de rotation 3D ([tt]CATransform3DMakeRotation(angle,x,y,z)[/tt]).

    Le tout marche pas trop mal, j'ai même permis de régler l'angle de rotation (x,y) si on veut. Reste quand même quelques imperfections :
    - si on choisit un angle de rotation autre que 0 ou 90°, ma fenêtre transparente (servant le temps de l'animation) n'est pas assez grande et du coup y'a un effet de clipping sur l'animation. Bon le choix de l'axe de rotation en mm temps je l'ai ajouté vite fait pour montrer le principe, voulais pas me fouler plus que ça là  dedans.
    - Un effet de flicker est éventuellement visible au moment de la capture, ceci est dû au fait que je force la fenêtre à  s'afficher (orderFront) pour pouvoir appeler la méthode CG qui fait la capture (et remet en orderOut ensuite si elle n'était pas visible avant), car j'ai remarqué que sinon ça donnait une image vide. Je suis prenneur d'une modif sur mon code qui permettrait de capturer aussi les fenêtres OffScreen pour éviter ça.
    - Il n'y a pas les ombres pendant la rotation. C'est à  la fois voulu parce que plus simple et surtout m'évite de calculer la taille d'image qui me sera nécessaire (plus grande que la fenêtre à  cause des ombres donc) et le positionnement de tout ça (l'effet d'ombre en haut et à  gauche, bien que bien plus léger qu'à  droite et en bas, existe et crée un décalage aussi entre les coordonnées de la fenêtre et celles de l'image générée...)

    Mais bon voilà  je vous livre ça en l'état, tout commentaire étant le bienvenu, c'est histoire de montrer où j'en suis arrivé si ça peut servir à  qqun quoi ;)
  • RocouRocou Membre
    06:29 modifié #15
    dans 1235841248:

    Merci No, vraiment très bien , bien commenté, excellent !

    Oui, merci beaucoup!
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #16
    Pas mal le core anim test.

    Si on pouvait supprimer l'effet de flickr et avoir une animation "en perspective", ce serait super. EN fait là  on a pas vraiment l'impression d'un retournement. On dirait plutot que la fenêtre se "collapse" sur elle-meme puis se réétire en une nouvelle..
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #17
    Ah oui exact tiens me disais bien qu'il y avait un truc qui me gênant un peu qd j'ai testé l'animation ^^
    Vais voir si c'est jouable.

    Bon après le principe pour moi c'était de découvrir CoreAnimation (c'était mon premier projet utilisant CA), et c'est sûr qu'utiliser CGSPrivate.h rend quand même mieux.
    Le seul truc qui me gène c'est l'aspect undocumented de la chose dans l'utilisation des méthodes CoreGraphics directement... faudrait que ça s'officialisse en fait... maintenant CGSPrivate.h commence à  être connu et utilisé on le trouve un peu partout sur le net, donc bon faut juste espérer que ça disparaisse pas prochainement quoi. Mais sinon le rendu est forcément mieux car c'est justement à  priori ce qu'Apple utilise en interne, donc c'est bien l'effet attendu qu'on obtient on a pas à  le "recréer" avec CoreAnim.

    Bon faut que je me plonge encore plus dans CoreAnimation pour voir si on peut ajouter de la perspective :)
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #18
    Bon voilà  une autre version qui ajoute la perspective (et enrichit un peu la GUI pour régler tout ça :D)

    En fait pour la perspective, c'est simple il suffit de modifier directement la matrice de transformation pour mettre un facteur multiplicateur sur le Z (atome "m34" de la matrice de transformation 3D) :
    CATransform3D transform3D = CATransform3DMakeRotation(M_PI_2, flipAxis.x, flipAxis.y, 0);<br />	transform3D.m34 = -perspectiveForce/100;<br />	[[[transparentWindowForAnimation contentView] layer] setTransform:transform3D];<br />
    
    Voilà  pour l'idée...
    J'ai rajouté quelques marges pour la "fenêtre transparente" de sorte qu'elle soit plus grande et que ça évite un peu plus de couper, mais bon idéalement faudrait calculer cette marge au mieux (la flemme ^^)

    Voilà  donc la nouvelle version  ;)
  • RocouRocou Membre
    06:29 modifié #19
    dans 1236022790:

    Voilà  donc la nouvelle version  ;)

    Super boulot, j'ai beaucoup appris grâce à  ton code. C'est un peu lourdingue pour un simple effet de flip mais j'ai beaucoup progressé. Merci.
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #20
    Bah sinon comme je l'ai déjà  dit, CGSPrivate.h reste quand même bien foutu et largement utilisé, donc mon objectif était de faire un code utilisant les APIs officielles et d'en profiter pour découvrir CoreAnimation que je n'avais jamais touché, mais CGSPrivate donnera des effets plus sympas.

    Pour plus d'infos je te conseille le PDF "Xcode animations" de Ankur Kothari (que tu trouveras par exemple ici), qui date quand même pas mal (c'est du Xcode et du IB 2 donc faut adapter les manips pour Xcode et IB 3) mais bon le code lui ne change pas ; ou sa version HTML sur son blog, ici.
    Le header officiel CGSPrivate.h publié par Richard Wareham est quant à  lui dispo chez CocoaDev entre autres, chez qui tu trouveras aussi des liens vers des pages et exemples intéressants pour faire tous ces effets directement avec les fonctions undocumented d'Apple. Et vu depuis le temps qu'elles existent et qu'elles sont encore présente, je pense qu'il n'y a pas trop de risque... il faut juste espérer ne pas les voir disparaà®tre dans Snow Leopard... ou alors si c'est le cas que ce soit en contrepartie de la publication d'une API équivalente officielle ;)

    Après, ça n'empêche pas d'utiliser CoreAnimation pour d'autres effets sympas  :)
  • NoNo Membre
    06:29 modifié #21
    Pour Pages et son effet de transition entre les 2 fenêtres, je viens de télécharger la démo et j'y ai fait quelques recherches.

    Bon, les résultats :
    1. c'est 100 % api privée.
    2. l'effet utilise CGSSetWindowWarp, la même fonction qui permet les effets de miniaturisation dans le dock.

    J'ai trouvé le truc en espionnant Pages (via Instruments) et en y trouvant la présence de cette fonction.
    Pour être sûr que c'est elle qui est utilisée lors de cette transition, j'ai fait comme pour la miniaturisation des fenêtres : l'appui sur SHIFT au moment du lancement l'effet ralentit ce dernier.

    CGSSetWindowWarp permet de déformer les fenêtres (toutes les déformations sont presques possibles car ça utilise un mesh de déformation) en temps réel.

    L'effet donc déforme la première fenêtre (celle des modèles). Puis au milieu de la transition, elle est rendue invisible. Au même moment, le panel est rendu visible et utilise la même déformation que la fenêtre juste avant sa disparition. Enfin, la transition repart dans l'autre sens permettant au panel de redevenir "normal".

    CQFD.
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #22
    Et tu saurais pas comment faire pareil par hasard ?  :)
    ;)
  • NoNo Membre
    06:29 modifié #23
    dans 1236246401:

    Et tu saurais pas comment faire pareil par hasard ?  :)
    ;)



    Comment ça faire pareil ?
    Tu veux utiliser la même fonction privée ?
    Ou tu veux simuler cette fonction en quelque chose de moins undocumented ?
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #24
    Pour faire pareil, avec les undocumented, bah c'est super simple à  partir du moment où tu utilises CGSPrivate.h, encore une fois. Suffit de reprendre l'exemple du cube qu'il donne sur son site ou dans son PDF ou qu'on trouve un peu partout sur le net et de remplacer la constante d'effet cube par la constante donnée par No, grosso modo, non ?
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #25
    je comprends bien pour appliquer l'effet à  ma fenêtre
    mais coment je faire avec la fenêtre du NSOpenPanel. ?
  • Nebuchad34Nebuchad34 Membre
    06:29 modifié #26
    Je fait des essais avec window warp. Cela déforme mes fenêtres comme je veux ce qui est très pratique. mais problème, les transformations sont instantannées ??
  • NoNo Membre
    06:29 modifié #27
    dans 1236268715:

    Je fait des essais avec window warp. Cela déforme mes fenêtres comme je veux ce qui est très pratique. mais problème, les transformations sont instantannées ??


    Oui, elles sont instantanées.
    Le fonctionnement du warping repose sur une matrice qui associe pixels réels (ceux de la fenêtre) et pixels d'affichage (ce qui apparaitra à  l'écran).

    le warping peut être mis sur une fenêtre non-affichée (cas du openPanel par exemple). Il suffit d'avoir son window-id.
    Au moment de son affichage, le warping agira sur son aspect.
  • NoNo Membre
    06:29 modifié #28
    Sur la toile, j'ai trouvé un petit tuto sympa, LemurFlip, sur la rotation (flip) entre 2 views.
    En gros, le tuto travaille comme ce qu'Aligator a exposé plus haut (à  savoir 2 layers et une fenêtre transparente).
    Le plus est que l'effet de rotation est vraiment réelle (car celui d'Ali reste un peu artificiel).

    Mais tout comme Ali, reste le problème d'acquérir l'image des fenêtres. Et là , je me heurte aux mêmes problèmes.

    Donc, je pense que le warping est pour le moment la seule solutions acceptable (du moins si on considère qu'utiliser des undocumented est acceptable).

    Je suis donc à  la recherche de la formule mathématique géniale qui permettrait d'avoir la série de 4 points (pour les 4 extrémités d'une fenêtre) dans l'espace écran pour visuellement simuler cette rotation.
  • Philippe49Philippe49 Membre
    mars 2009 modifié #29
    dans 1236601520:

    Je suis donc à  la recherche de la formule mathématique géniale qui permettrait d'avoir la série de 4 points (pour les 4 extrémités d'une fenêtre) dans l'espace écran pour visuellement simuler cette rotation.


    On peut peut-être t'aider là . Tu peux préciser les contraintes ? C'est celles de la video initiale de ce post ? Cela semble une rotation 3D autour d'un axe qui est en z<0, et x peut être variable, ce qui donne un effet de perspective.

    Sans rien dire depuis le début de ce post,ce qui il faut avouer est assez rare  ::)) , je cherche dans la direction de l'animation des filtres que je n'avais pas complètement dévissé dans la partie filtre du tuto core animation
  • NoNo Membre
    06:29 modifié #30
    dans 1236659917:

    On peut peut-être t'aider là . Tu peux préciser les contraintes ? C'est celles de la video initiale de ce post ? Cela semble une rotation 3D autour d'un axe qui est en z<0, et x peut être variable, ce qui donne un effet de perspective.


    Je travaille dans 2 directions :
    1. celle qui utilise les méthodes officielles (layers et animation),
    2. celle qui utilise le warping des fenêtres (donc undocumented).

    Dans le premier cas, l'effet flip est presque au point.
    J'ai utilisé le tuto LemurFlip pour réaliser ça.
    Comme dit plus haut, en mixant ce tuto et les explications d'Aligator, j'obtiens bien un bel effet entre les 2 fenêtres. Le problème consiste à  transformer ces fenêtres en images pour les layers.
    Là  la situation est correcte pour la fenêtre de départ car elle est visible (donc "photographiable").
    Par contre, la fenêtre d'arrivée est non visible (donc impossible d'avoir son image).
    J'ai partiellement résolu le problème est la rendant visible, puis photo, puis à  nouveau invisible. Pour éviter l'effet de flash qu'Aligator a rencontré, j'ai suspendu la mise à  jour de l'affichage (NSDisableScreenUpdates).
    Mais ça ne me plait qu'à  moitié : ça change l'ordre des fenêtres, et la gestion de la key-window n'est pas parfait.

    Dans le second cas, j'aimerai jouer avec le warping.
    Ici, il me suffit pour chaque point des coins de la fenêtre de départ obtenir la suite de points simulant l'effet de rotation.
    Un dessin étant toujours plus clair qu'un long discours, je joins un croquis.

    Dans ce croquis, l'axe de rotation est le centre vertical de la fenêtre (trait bleu foncé).
    La fenêtre en position de départ est en contour rouge.
    Puis cette fenêtre doit "tourner" (contour vert, orange et rouge) jusqu'à  disparaitre (car on ne voit plus que sa tranche).
    La formule consiste donc à  récupérer les coordonnées des points bleus, verts, oranges et rouges (dans le cas d'une rotation en 4 mouvements) dessinées sur le croquis, et ceci pour le coin haut gauche.
    En appliquant la même formule pour les 3 autres points, je pense que l'effet de rotation sera raisonnable.
  • AliGatorAliGator Membre, Modérateur
    06:29 modifié #31
    Je ne sais pas si vous aviez lus mon dernier post dans ce thread, mais j'avais résolu le pb de perspective... et dans la foulée celui des fenêtres invisibles, que je rendais visible puis re-invisibles... en gérant la keyWindow justement.

    Après il me restait le pb du ScreenUpdate en effet qui faisait flasher l'écran, je ne connaissais pas cette possibilité de désactiver le refresh écran.

    Et le seul autre pb qui me restait de mémoire, c'était la taille de ma fenêtre transparente, mais c'était plus de la flemme qu'autre chose : l'idéal pour éviter tout clipping est de faire en sorte que la fenêtre transparente prenne tout l'écran, et positionner correctement les NSViews qui serviront à  la rotation... en faisant gaffe à  l'anchorPoint de la rotation... Ou encore (sans doute plus simple si ça marche mais j'ai pas essayé) désactiver le clipping des layers (je sais que pour les CALayers c'est possible, clipping activé par défaut comme pour les Views, mais désactivable)
Connectez-vous ou Inscrivez-vous pour répondre.