[Résolu] Rotate et zoom

busterTheobusterTheo Membre
juillet 2015 modifié dans API UIKit #1

Bonjour,


je parviens à  roter une image.


Je parviens à  zoomer dessus.


 


Mais quand je la rote, je pers mon zoom, et quand je zoom, je pers ma rotation.


 


Voici ma fonction de rotation :



maView.transform = CGAffineTransformMakeRotation(degreesToRadians(angle))

J'ai fait pleins de tutos assez compliqué, et c'est ce que j'ai trouvé de plus simple.


 


 


Si quelqu'un a une idée, parce que ça fait deux jours que je suis là -dessus, et galère cher.


 


Merci d'avance


Réponses

  • PyrohPyroh Membre

    Bonjour,

    je parviens à  roter une image.



     

    Moi je ne parviens à  roter que de l'air... (souvent nauséabond).


     


    Plus sérieusement, comment est-ce que tu enchaà®ne tes transformations ?


  • AliGatorAliGator Membre, Modérateur
    mars 2015 modifié #3
    Il suffit de composer les transformations affines, comme en maths.

    Tu veux la faire tourner autour de quel point déjà  ? (0,0) ? Son centre ?
    Et de même tu veux la faire ton zoom centré sur quel point ?

    Typiquement une CGAffineTransformMakeRotation va créer une transformation affine qui applique une rotation autour de (0,0). Si tu veux faire une rotation et un zoom autour de (x,y) il faut que tu fasses :
    - une translation T1 de (-x,-y) " de sorte que le point qui était en (x,y) se retrouve en (0,0)
    - puis une rotation R (autour du nouveau (0,0) donc)
    - ainsi que ton opération de zoom Z
    - puis la translation T2 de (x,y), donc la translation inverse de T1, pour remettre ton centre de rotation " seul point invariant de ta translation et ta rotation " en (x,y) à  son emplacement d'origine

    Donc il fut faire la transformation composée de T1, R, Z et T2.

    Attention à  appliquer le code dans le bon sens : toi ce que tu veux c'est le résultat de T2(Z(R(T1(x)))) " car tu appliques d'abord la translation T1(x) puis appliques la rotation R sur le résultat pour avoir R(T1(x)), etc.

    Donc il faut bien écrire un truc dans ce genre :
    CGAffineTransform t1 = CGAffineTransformMakeTranslation(-x,-y); // T1(x)
    CGAffineTransform r_t1 = CGAffineTransformRotate(t1, angle); // R( T1(x) )
    CGAffineTransform z_r_t1 = CGAffineTransformScale(r_t1, scaleX,scaleY); // Z( R(T1(x)) )
    CGAffineTransform t2_z_r_t1 = CGAffineTransformTranslate(z_r_t1, x,y); // T2( Z(R(T1(x))) )
    Ou alors si tu trouves ça plus lisible :
    CGAffineTransform t1 = CGAffineTransformMakeTranslation(-x,-y); // T1
    CGAffineTransform r = CGAffineTransformMakeRotation(angle); // R
    CGAffineTransform z = CGAffineTransformMakeScale(scaleX, scaleY); // Z
    CGAffineTransform t2 = CGAffineTransformMakeTranslation(x,y); // T2
    // CGAffineTransform t2 = CGAffineTransformInvert(t1); // Autre façon de définir T2, comme étant l'inverse de T1
    CGAffineTransform final = CGAffineTransformConcat(t2,CGAffineTransformConcat(z,CGAffineTransformConcat(r,t1))); // (T2*Z*R*T1)(x)
    Tout ça ce n'est que des maths qui peuvent paraà®tre compliquées mais finalement si tu décomposes bien restent assez simple. Faut juste pas se tromper dans l'ordre dans lequel tu appliques des transformations (sinon tu risques de faire une rotation autour de (-x,-y) au lieu de la faire autour de (x,y)!) et donc bien comprendre que la concaténation U*V de 2 transformations U et V (donc CGAffineTransformConcat(U,V)) c'est U(V(x)) donc ça veut dire que c'est V qui est appliqué en premier sur x, puis U qui est appliqué ensuite au résultat de V(x)... donc V d'abord et U ensuite et pas U d'abord et V ensuite, c'est juste l'erreur la plus courante quand on manipule les transformations affines. A part ce point là  bien piègeux, le reste est assez simple.


    PS : Bon justement j'ai pas testé mon code, donc à  vérifier en vrai, car justement cette inversion d'ordre dans les compositions étant tellement une erreur courante, en général faut toujours vérifier... je te laisse tester et corriger le code le cas échéant, moi je l'ai écrit en live sans tester :P
  • busterTheobusterTheo Membre
    avril 2015 modifié #4

    Merci à  vous pour vos réponses.


    Comme je le disais, je parviens à  faire tourner ma photo, à  l'aide de la seule fonction



    maView.transform = CGAffineTransformMakeRotation(degreesToRadians(angle))

    J'ai déjà  croisé la série de transformations, translation/rotation, et j'ai trouvé ça carrément pénible, quand je vois que cette fonction, seule, fait tout le travail de tourner autour de son centre, et cela, simplement (ce qui m'intéresse).


     


    En tout cas merci quand même pour les explications sur les enchaà®nements de transformations, j'en aurais certainement besoin un de ces quatres.


     


    D'autre part, le zoom (dans un UIScrollView et ses méthodes associées - et bien centré sur son centre) n'a rien à  voir dans un hypothétique enchaà®nement de transformations, car zoom et rotation sont (dans mon projet) deux actions dans deux domaines différents.


     


    Je joins un lien sur une petite vidéo pour bien comprendre.


     


    Je pense que :


    Lorsque je zoom, je dois aller récupérer la valeur de mon angle de rotation, et refaire la rotation.


    Lorsque je rotationne d;-), je dois récupérer quelque part la valeur de mon zoom, et la ré-attribuer à  mon image.


     


    Et là , je ne vois vraiment pas comment faire cela, si c'est ce que je dois faire !


     


    La vidéo


     


    Merci encore


  • CéroceCéroce Membre, Modérateur

    Je pense que :
    Lorsque je zoom, je dois aller récupérer la valeur de mon angle de rotation, et refaire la rotation.
    Lorsque je rotationne d;-), je dois récupérer quelque part la valeur de mon zoom, et la ré-attribuer à  mon image.

    Les deux opérations consistent à  modifier la matrice de transformation. Dans les deux cas, il faut donc modifier la matrice en appliquant successivement les deux transformations (rotation et échelle), tel qu'indiqué par AliGator.
  • Merci Céroce pour ta réponse, mais le problème est que quand je zoom, je veux zoomer sur l'image déjà  rotée, et inversement.


    Je ne fais pas les deux en même temps (voir la vidéo) - Ce sont deux actions distinctes.


     


    On peux zoomer (dans un UIScrollView) sans être dans la matrice d;-), et on peut roter (avec la simple fonction citée plus haut) sans y être non plus, ou en tout cas sans trop souffrir.


     


    Si je comprend bien vos réponses, cela voudrait dire, que mon zoom dans un UIScrollView, devrait être remplacé par une série de transformations... oulala.


     


    J'avoue que j'ai l'impression que pour faire deux choses totalement différentes, et totalement indépendantes, qui se font facilement, je devrais tout revoir et souffrir - boooouuhh


  • CéroceCéroce Membre, Modérateur
    avril 2015 modifié #7
    UIScrollView modifie la matrice quand on zoome (les 'bounds' de la UIView changent, mais pas sa 'frame').
    Maintenant, comme UIScrollView ne gère pas la rotation, la solution est peut-être de créer une vue intermédiaire qui servira juste à  appliquer la rotation.
  • Heu, Céroce, je ne comprend pas trop ce que tu proposes. Une vue intermédiaire ?


     


    Moi, j'ai mes deux vues pour le UIScrollView et la UIImageView



    // La photo plein-écran
    var patientCalibrationScrollView: UIScrollView = UIScrollView()
    var patientCalibrationImageView: UIImageView = UIImageView()

    J'ai ma vue pour la UIImage



    // Pour la rotation de l'image
    var myImage = UIImage()
    var angle: CGFloat!

    Dans le didLoad



    myImage = patientCalibrationImageView.image!
    rotateImage(myImage, angle: 0)

    La fonction de rotation



    func rotateImage(sourceImage: UIImage, angle: CGFloat) {
    if sourceImage == patientCalibrationImageView.image! {
    let degreesToRadians: (CGFloat) -> CGFloat = {
    return $0 / 180.0 * CGFloat(M_PI)
    }
    patientCalibrationImageView.transform = CGAffineTransformMakeRotation(degreesToRadians(angle))
    }
    }

    Puis dans le gestureRecognizer



    func rotatePan(recognizer: UIPanGestureRecognizer) {
    let pan = recognizer as UIPanGestureRecognizer
    angle = 0.0

    switch(pan.state) {
    case .Began:
    let recognizerX = recognizer.locationInView(self.view).x as CGFloat
    let recognizerY = recognizer.locationInView(self.view).y as CGFloat
    case .Changed:
    let recognizerX = recognizer.locationInView(self.view).x as CGFloat
    let recognizerY = recognizer.locationInView(self.view).y as CGFloat

    angle = angle + recognizerX - orignX

    println("angle = \(angle)")

    myRoundCurseurView.frame.origin.x = recognizerX
    myRoundCurseurView.frame.origin.y = recognizerY

    myImage = patientCalibrationImageView.image!
    rotateImage(myImage, angle: angle)
    case .Ended:
    let recognizerX = recognizer.locationInView(self.view).x as CGFloat
    let recognizerY = recognizer.locationInView(self.view).y as CGFloat

    angle = angle + recognizerX - orignX
    default:
    break
    }
    }

    myRoundCurseurView est le curseur pour faire roter l'image


     


    Et mes méthodes UIScrollView



    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return patientCalibrationImageView
    }

    et



    func scrollViewDidZoom(scrollView: UIScrollView!) {
    centerScrollViewContents(scrollView)
    }

    avec donc la fonction centerScroll.... récupérée je sais plus où - et qui fonctionne grave - je ne sais plus si je l'ai modifiée.



    func centerScrollViewContents(scrollView: UIScrollView) {
    var boundsSize = patientCalibrationScrollView.bounds.size
    var contentsFrame = patientCalibrationImageView.frame

    if contentsFrame.size.width < boundsSize.width {
    contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0
    } else {
    contentsFrame.origin.x = 0.0
    }

    if contentsFrame.size.height < boundsSize.height {
    contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0
    } else {
    contentsFrame.origin.y = 0.0
    }
    patientCalibrationImageView.frame = contentsFrame
    }

    Et donc, je suis complètement perdu.


     


    J'arrive, et c'est un bon début, à  récupérer le facteur de zoom dans la méthode scrollViewDidZoom



    print("zoom = \(patientCalibrationScrollView.zoomScale)")

    J'essaie de le stocker pour le récupérer lors du rotate, mais c'est pas gagné, et je suis sûr que je vais me faire engueuler, ça sent le pas conforme d;-)


  • CéroceCéroce Membre, Modérateur
    Je n'ai pas le temps d'aller étudier ton problème en détail mais ce que je veux dire, c'est que UIScrollView fait sa fonction de zoom en changeant la matrice de transformation de ses vues enfant.
    Cela signifie que si tu changes la matrice de ta vue enfant pour faire une rotation, puis qu'ensuite, tu fais défiler, comme UIScrollView va changer sa matrice, alors il n'y aura plus de rotation. Et vice versa: si tu appliques une rotation à  la matrice, alors il n'y a plus de zoom.
    Il me semble bien que c'est qu'on observe dans ta vidéo.
  • busterTheobusterTheo Membre
    avril 2015 modifié #10

    Ah d'accord.


     


    Dis moi, dans mes recherches, je tombe là -dessus. C'est en objectiveC, mais je pourrais m'en sortir, je pense.


     


    Ce que j'y comprend, c'est que l'on squizze le zoom du UIScrollVew par UIPinchGestureRecognizer et on y fait le zoom avec du "scale" en "transform" (excuse mes raccourcis conceptuels et théoriques), et pareil, donc, pour le rotate.


     


    Et donc, ensuite, je traficote les "transform", comme me l'a expliqué AliGator.


     


    C'est bien çà  ?


     


    Si oui, eh bien, c'est pas gagné... Mais je pense que c'est le chemin à  suivre. 


     


    Merci encore


     


    Celui-là  n'a pas l'air mal, non plus. T'en penses quoi ?


  • Essaye de faire myView.transform= CGAffineTransformScale(myView.transform, 0.5,0.5) ou un truc du genre (j'ai donné le nom de la fonction de tête)
Connectez-vous ou Inscrivez-vous pour répondre.