Comment modifier l'origine d'une NSAffineTransform?

Bonjour,


 


Je souhaite me faire un logiciel d'animation en démultipliant avec des transformations affines des NSImage (s) : des itérations, des fractales, etc.


 


Aucune des trois solutions proposées par les frameworks ne me convient vraiment :


- les NSAffineTransform ont pour origine celle de la NSView. J'aimerais pouvoir choisir plutôt le centre du rectangle de l'image comme origine de la transformation. (tout en gardant le rectangle de la NSView inchangé pour que le dessin s'y affiche, d'où l'impossibilité de modifier l'origine de la NSView). Le problème se pose à  l'identique avec Quartz. 


J'ai du mal à  comprendre les méthodes de "Converting Coordinate Values" en particulier.


 


- sinon il y a les filtres CIIImage, et le filtre "Perspective transforme". Je ne trouve pas la doc des paramètres à  intégrer à  mon code. J'ai déjà  utilisé ces filtres pourtant... Par ailleurs, lorsque je la teste dans la démo "CoreImageFunHouse", La transformation a l'air de déborder du rectangle d'origine de l'image. J'espère que dans la NSView globale, l'affichage sera correct. le savez-vous?


 


Dernier point, la structure de la matrice d'une NSAffineTransform est privée : pas moyen de calculer les valeurs et de réécrire la structure apparemment (le compilateur veut pas!). Y a t-il une astuce pour contourner le problème? (pareil avec Quartz apparemment)


 


Merci beaucoup si vous avez des idées. Je m'en vais essayer les filtres en farfouillant d'abord dans la doc. (mais où donc l'ont-ils rangé?)


Réponses

  • AliGatorAliGator Membre, Modérateur
    juillet 2015 modifié #2
    Les NSAffineTransform ne sont rien d'autre que des matrices de transformation comme en mathématiques. Tu peux donc les composer entre elles, comme en maths, par exemple pour faire une rotation autour du centre de ton image plutôt qu'autour de l'origine :
    - Une matrice de transformation de type "translation" pour déplacer le centre de ton dessin pour que ce soit lui le nouveau (0,0)
    - Une matrice de rotation autour de ce (0,0)
    - Une matrice de translation inverse de la première pour re-déplacer le point (0,0) au centre

    Attention petit rappel mathématique la composition de matrices (ou de fonctions) se fait dans un ordre qui peut paraà®tre contraire à  l'ordre naturel suggéré par l'écriture, ainsi tout comme en maths f(g(x)) consiste à  d'abord appliquer la fonction g() à  x avant d'appliquer f() au résultat (et pas d'abord f puis g), il faut de même faire attention à  composer les NSAffineTransform dans le bon sens (il y a déjà  des questions sur le sujet sur le forum tu devrais trouver plus de détails en faisant une recherche ici)

    Quant à  ta dernière question, tu peux tout à  fait modifier les composantes de la matrice de ta NSAffineTransform : voir ici dans la doc.
  • Ah oui, très bien l'idée d'utiliser deux translations. J'avais essayé en modifiant l'origine du rectangle de la photo, mais sans succès... J'm'en vais tester cela.


     


    Par contre, lorsque j'ai fait :



    NSAffineTransform* xform = [NSAffineTransform transform];

    double anRad = rotVal * (6.2831852 / 360.0);
    xform->_transformStruct.m11 = cosf(anRad) * homXVal;
    xform->_transformStruct.m21 = -1.0 * sinf(anRad);
    xform->_transformStruct.tX = transXVal;
    xform->_transformStruct.m12 = sinf(anRad);
    xform->_transformStruct.m22 = cosf(anRad) * homYVal;
    xform->_transformStruct.tY = transYVal;

    le compilateur n'est pas content du tout : il barre de rouge et me dit que l'accès à  la structure est privé! D'où ma question...


     


    Merci AliGator en tous les cas, j'essaie cela. 


     


    (effectivement, l'ordre des transformations sera aussi une bonne prise de tête. L'idée d'écrire soit-même la matrice ferait gagner du temps).


  • AliGatorAliGator Membre, Modérateur
    juillet 2015 modifié #4


    le compilateur n'est pas content du tout : il barre de rouge et me dit que l'accès à  la structure est privé!

    Bah oui c'est normal, tu essayes d'accéder à  la variable d'instance interne via la syntaxe "->" + le nom de la variable privée "_transformStruct" associée à  la propriété (d'ailleurs le nom commençant par "_" devrait te mettre la puce à  l'oreille quant au fait que c'est un truc privé)... au lieu d'accéder à  la propriété publique comme on le fait d'habitude sur n'importe quel objet Cocoa...


    Essaye ceci à  la place, ça devrait mieux marcher :

    NSAffineTransform* transform = [NSAffineTransform transform];
    // Accède à  la propriété transformStruct de la NSAffineTransform
    // (et non pas à  la *variable privée* "_transformStruct")
    NSAffineTransformStruct matrix = transform.transformStruct;
    // Modifie la matrice de transformation à  ta guise
    matrix.m11 = ...;
    matrix.m12 = ...;
    // Applique la nouvelle matrice de transformation à  ta NSAffineTransform
    transform.transformStruct = matrix;
    Ou encore en utilisant directement la syntaxe d'initialisation des structs via les noms des champs de la struct (syntaxe C pour créer des structs C) :

    NSAffineTransform* transform = [NSAffineTransform transform];
    transform.transformStruct = (NSAffineTransformStruct){
    .m11 = ...,
    .m12 = ...,
    .m21 = ...,
    .m22 = ...,
    .tX = ...,
    .tY = ...
    };


    (effectivement, l'ordre des transformations sera aussi une bonne prise de tête. L'idée d'écrire soit-même la matrice ferait gagner du temps).

    Je ne suis pas trop d'accord. Car en utilisant la transformStruct, encore faut-il savoir "lire dans la matrice" (c'est le cas de le dire pour le coup ) et comprendre de suite ce que tu cherches à  faire en voyant les différents calculs que tu fais à  la main.


    De plus, je ne suis même pas sûr que les valeurs que tu as mises dans le code de ton précédent post sont bonnes (le calcul des bonnes valeurs pour tX et tY n'est pas si simple vu qu'après rotation en (0,0) le point qui était avant au centre de l'image n'est plus au centre, donc normalement tX et tY aussi dépendend du sinus et cosinus de l'angle de rotation... Bref, ça peut vite être prise de tête et calculs de maths à  faire à  la main qui non seulement peuvent être source d'erreur mais en plus vont être difficiles à  relire.


    Alors que NSAffineTransform a une API ObjC, propre et explicite (encore + que son équivalent CoreGraphics qu'est CGAffineTransform), où écrire ce genre de chose est bien plus lisible que d'écrire directement des résultats calculés dans la matrice je trouve :
    - (NSAffineTransform*)transformToRotate:(CGFloat)angleInDegrees aroundPoint:(NSPoint)centerPoint
    {
    NSAffineTransform *t = [NSAffineTransform transform];
    // Note: we need to apply the operations in the reverse order, becase M1*M2*M3 applies M3 then M2 then M1 (Math!)
    [t translateXBy:centerPoint.x yBy:centerPoint.y]; // (3) move back (0,0) to (cx,cy)
    [t rotateByDegrees:angleInDegrees]; // (2) then rotate
    [t translateXBy:-centerPoint.x yBy:-centerPoint.y]; // (1) translate(-cx, -cy) so that we move (cx,cy) to (0,0)
    return t;
    }
    Dans cet exemple, l'utilisation de "translateXBy:YBy:" et "rotateByDegrees:" me semble beaucoup plus clair, où le seul truc qui peut être choquant c'est qu'il faut penser à  lire les opérations de bas en haut pour avoir l'ordre d'application, mais à  part ça c'est quand même bien plus lisible que des cos et sin et maths partout !
  • HerveHerve Membre
    juillet 2015 modifié #5

    Merci une nouvelle fois AliGator


     


    Je préfère travailler directement sur la matrice parce qu'en plus, on peut déformer le rectangle, le "tordre". J'utilisais cela dans des générateurs d'IFS. J'appelle cette torsion "cisaillement", je ne connais pas le terme mathématique.


     


    La matrice fonctionne très bien. Cela donne :



    NSAffineTransformStruct matrix = xform.transformStruct;


    double anRad = rotVal * (6.2831852 / 360.0);//rotVal : angle en degrés
    matrix.m11 = cosf(anRad) * homXVal;//homXVal : rapport de l'homothétie
    matrix.m21 = -1.0 * sinf(anRad) + cisXVal;//cisXVal : la torsion, entre -1 et 1
    matrix.tX = transXVal * bounds.size.width;
    /*transXVal : la translation, un rapport entre -1 et 1 multiplié par la largeur du cadre ici, ou bien directement en nombre de pixels.*/

    matrix.m12 = sinf(anRad) + cisYVal;
    matrix.m22 = cosf(anRad) * homYVal;
    matrix.tY = transYVal * bounds.size.height;
    xform.transformStruct = matrix;

    Merci pour le coup de main. En plus la photo se "tord" d'elle même , pas besoin de CIImage. Tant mieux, je préfère travailler avec Cocoa.


    Je n'ai pas encore réussi à  modifier l'origine du plan. En bas à  gauche, ce n'est vraiment pas joli. Je suis dessus en ce moment. 


  • AliGatorAliGator Membre, Modérateur
    Du coup si tu veux vraiment directement travailler avec les cos et sin utilise les bonnes constantes qui vont avec comme M_PI et pas ce magic number de 6.2831852 qui sort de nulle part !


    Et sinon t'es pas obligé de récupérer la NSAffineTransformStruct de ta nouvelle NSAffineTransform alors que tu la modifie entièrement finalement (redéfinis la valeur de tous les champs) donc tu t'en fiches de la valeur initiale. Tu peux directement écrire "NSAffineTransformStruct matrix;" tout court pour déclarer la valable avant de changer les valeurs de ses champs m11,m12,m21,m22,tX et tY. Ou encore utiliser la syntaxe que j'ai démontrée dans mon post précédent.
  • Pour l'origine, ce serait par là  :


    [self translateOriginToPoint:unPoint];


     


    6.2831852 = 2*PI.


    En ce moement, j'en suis aux essais, aux tests préliminaires, pour voir ce qui marche.


    On fera plus propre lorsqu'on entrera "dans le dur" (la version complète)


     


    Merci encore AliGator.


  • AliGatorAliGator Membre, Modérateur
    Et pour inverser ton repère, encore une fois les transformations sont a priori la solution : il suffit de créer une transformation qui va translater ton origine de son emplacement initial en bas à  gauche vers son emplacement en haut à  gauche, puis va faire un miroir vertical pour inverser l'axe Y et le faire pointer vers le bas.


    Tu appliques cette transformation à  ton contexte de dessin, puis tu fais touted les opérations de dessin de ton image comme tu les ferais dans ce nouveau repère, puis tu appliques la transformation inverse a la fin pour revenir dans le repère OS X avec l'origine en bas et Y vers le haut.


    En termes mathématiques, cela revient à  appliquer la matrice R = T*M*T' où T est la transformation qui inverse le repère, M est la transformation qui va déformer ton image, exprimée dans ce nouveau repère dans lequel tu es plus a l'aise, et T' la transformation inverse de T qui restaure le repère d'origine.

    Et le résultat R est du coup au final la transformation M mais exprimée dans le repère OSX.


    C'est la formule classique de changement de repère qui est d'ailleurs la même que ce que j'ai utilisé dans les exemples précédents de rotation autour d'un point arbitraire, ou T changeait le repère pour mettre le centre de rotation a l'origine du nouveau repère, M faisait la rotation, et T' restaurait le repère d'origine.


    Bah là  c'est pareil, exactement le même principe.

    Et tu es tout ce qu'il faut dans l'API pour concatener des NSAffineTransform !
  • AliGatorAliGator Membre, Modérateur


    Pour l'origine, ce serait par là  :
    [selftranslateOriginToPoint:unPoint];

    6.2831852 = 2*PI.
    En ce moement, j'en suis aux essais, aux tests préliminaires, pour voir ce qui marche.
    On fera plus propre lorsqu'on entrera "dans le dur" (la version complète)

    Merci encore AliGator.

    je ne sais pas quelle API tu utilises et dans quel contexte tu dessines (une NSImageBitmapRep ? Une NSImage sur laquelle tu as fait un lockFocus ? Autre ?) car je n'ai pas le reste de ton code, du coup je ne connais pas ce translateOriginToPoint. Mais s'il ne fait qu'une translation d'origine ça ne suffira pas si toi tu veux un repère avec Y vers le bas et non vers le haut comme c'est en standard sur OSX ;-)



    Et sinon j'avais bien deviné que ce magic number était la valeur de 2 PI ça va je la connais quand même 😄 mais ma remarque était plutôt pour te rappeler que les Magic Numbers c'est le mal et à  éviter et qu'en plus il existe déjà  une constante M_PI qui existe en standard donc autant l'utiliser.


    double angleEnRadians = angleEnDegres * M_PI/180.0;
  • "double angleEnRadians = angleEnDegres * M_PI/180.0;"


    Ah oui, on peut simplifier le calcul! :)


    Alors, je souhaite faire des NSImage qui s'affichent dans des NSRect qui, eux-mêmes sont transformés par des NSAffineTransform (tournés, tordus, etc.) La fonction de transformation d'origine marche bien : je définis n'importe quel point sur le plan comme centre de la rotation par exemple.


     


    La dernière chose qui me manque serait de "réinitialiser" le plan de façon à  pouvoir afficher une autre image et ses transformations (autres bien évidemment) ensuite.

    On peut faire :


    [self translateOriginToPoint:self.bounds.origin];


     


    mais cela semble être décevant : les affichages suivants se révèlent être aléatoires. parfois cela s'affiche, parfois non... De même la fonction "insert" de NSAffineTransform est insatisfaisante, sans doute à  cause des itérations de la transformation.


     


    Je ne trouve pas, ni dans les fonctions de NSView, ni dans celle de NSAffineTransform une fonction qui "reset" le plan affine, et remet l'origine à  0 et les transformations à  aucune. Je vais essayer un "concat" avec une matrice qui ne modifie rien pour voir, et je vous tiens au courant.


     


  • AliGatorAliGator Membre, Modérateur
    juillet 2015 modifié #11
    Concat faisant exactement ce que son nom suggère, à  savoir *concatener* une transformation aux transformations déjà  en place, un concat de la matrice identité ne fera évidemment rien du tout, c'est comme multiplier par 1.


    Le plus propre est comme je l'ai expliqué plus haut de toujours appliquer la transformation inverse (NSAffineTransform a certainement une méthode qui retourne pour toi la transform inverse d'ailleurs, non ?) pour revenir à  l'état initial.


    Mais sinon les techniques les plus courantes pour revenir à  l'état d'avant c'est directement de sauver l'état du contexte et le restaurer ensuite.

    Je ne sais pas sur quoi tu dessines (NSBitmapImageRep ? NSImage avec lockFocus appelé dessus ? CGContextRef ? Autre ?) mais les contextes graphiques ont en général une méthode push et pop ou save et restore ou ce genre de nom pour faire ça.
  • HerveHerve Membre
    juillet 2015 modifié #12

    En fait, il faut effectivement réécrire la matrice de la transformation pour que les dessins suivants ne subissent pas la dernière transfo en cours (matrice (1,0,0 // 0,1,0). En ce qui concerne l'origine, changer le point d'origine du plan produit l'effet escompté.


    Par contre, il m'arrive quelque chose d'étonnant : si je ne change pas le point d'origine pour la première transformation, ou si il est égal à  (0,0), les dessins dessinés après 



    [self translateOriginToPoint:self.bounds.origin];

    continuent de subir la transformation du plan, alors que si les valeurs du point sont différentes de (0,0), les dessins après sont "normaux". Du coup, je bricole avec un "leurre", en mettant un changement d'origine inutile "pour le cas où" le point d'origine serait le point nul.


    Voici le code complet de mon test ; il fonctionne, et produit l'effet souhaité. Avec des sliders, je modifie les valeurs de rotation, translation, homothétie et cisaillement de la première transfo. Celle-ci est réitérée pour des effets de répétition. Puis je dessine un carré test avec quelques rotations : ils doivent toujours être dessinés au même endroit quelle que soit la transformation précédente. Ce code marche, mais il y a cette bizarrerie!



    - (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    NSRect bounds = [self bounds];
    [[NSColor whiteColor]set];
    [NSBezierPath fillRect:bounds];

    [self translateOriginToPoint:self.bounds.origin];

    NSString* imageName = [[NSBundle mainBundle]
    pathForResource:@Quais ofType:@JPG];
    NSImage* tempImage = [[NSImage alloc] initWithContentsOfFile:imageName];

    NSAffineTransform* xform = [NSAffineTransform transform];

    NSRect cadre = NSMakeRect(180.0, 180.0, 230.0, 130.0);
    [[NSColor cyanColor]set];
    [NSBezierPath fillRect:cadre];

    [self translateOriginToPoint:pointCentral];
    cadre.origin.x -= pointCentral.x; //nécessaire pour ne pas avoir de translation inopinée des itérations.
    cadre.origin.y -= pointCentral.y;

    NSAffineTransformStruct matrix = xform.transformStruct;

    double anRad = rotVal * (6.2831852 / 360.0);
    matrix.m11 = cosf(anRad) * homXVal;
    matrix.m21 = -1.0 * sinf(anRad) + cisXVal;
    matrix.tX = transXVal * bounds.size.width;
    matrix.m12 = sinf(anRad) + cisYVal;
    matrix.m22 = cosf(anRad) * homYVal;
    matrix.tY = transYVal * bounds.size.height;
    xform.transformStruct = matrix;

    for (int i = 0; i < 3; i++){

    [xform concat];

    [tempImage drawInRect:cadre];

    }

    [self translateOriginToPoint:NSMakePoint(10.0, 10.0)]; //pour le cas où point central = (0;0) !!
    [self translateOriginToPoint:self.bounds.origin];

    matrix.m11 = 1.0;
    matrix.m21 = 0.0;
    matrix.tX = 0.0;
    matrix.m12 = 0.0;
    matrix.m22 = 1.0;
    matrix.tY = 0.0;
    xform.transformStruct = matrix; //nécessaire

    //[xform concat]; //inutile

    NSRect cadrePointCentral = NSMakeRect(pointCentral.x - 8.0, pointCentral.y - 8.0, 16.0, 16.0);
    [[NSColor purpleColor]set];
    [[NSBezierPath bezierPathWithOvalInRect:cadrePointCentral]fill];

    NSRect cadreB = NSMakeRect(80.0, 80.0, 130.0, 130.0);
    [[NSColor redColor]set];
    [NSBezierPath fillRect:cadreB];

    [xform translateXBy:10.0 yBy:0.0];
    [xform rotateByDegrees:5];

    for (int j = 0; j < 5; j++){
    [xform concat];
    [NSBezierPath fillRect:cadreB];
    }
    }


     


    Qui dit mieux?


     



    Merci mille fois AliGator en tous les cas pour tes idées et tes posts.


  • AliGatorAliGator Membre, Modérateur
    Bah déjà  plutôt que resetter ta struct matrix a la matrice identité champ par champ, tout ça pour la réaffecter a ta NSAffineTransform xform pour pouvoir repartir d'une matrice identité pour le reste du code qui l'a modifié, ça serait aussi simple de créer une autre variable NSAffineTransform gril scratch et de l'utiliser pour la fin du code.


    Par ce que là , c'est comme si après avoir fait un schéma sur un papier, tu gommais tout ce que tu avais écrit sur le papier, pour pouvoir repartir d'une feuille vierge,... au lieu de tout simplement prendre une nouvelle feuille vierge directement !


    Car là  ce que tu en fais de ta matrix et de ta xform après c'est juste faire autr chose de complètement indépendant (c'est pas appliquer la transformation inverse ou changer la matrice de transformation du contexte graphique courant ou quoi, c'est juste changer la valeur d'une variable.

    C'est comme si tu avais une variable x, tu faisais des calculs dedans donc elle se terminait a une valeur de 42, et ensuite si tu voulais faire un autre calcul (genre 3*5) tu faisais "x=0" (remise à  zéro de ta matrice pour la remettre à  la matrice identité) avant de faire "x += 3*5" (concaténation de la matrice précédente avec la nouvelle matrice) au lieu de faire directement x=3*5 ou d'utiliser une autre variable pour faire y+=3*5



    Et sinon, je t'ai déjà  donné d'autres astuces et pistes dans les messages plus haut que tu n'as pas intégré à  ton code, comme utiliser [ul=https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSGraphicsContext_Class/index.html#//apple_ref/occ/instm/NSGraphicsContext/saveGraphicsState]saveGraphicsState[/url] au début de ton code, faire toutes les transformations que tu veux, et utiliser la méthode "restoreGraphicsState" plus tard pour restaurer l'état (y compris le repère et la matrice de transformation active) à  ce qui avait été sauvegardé.
  • Merci AliGator une nouvelle fois. Tu écris :




    Bah déjà  plutôt que resetter ta struct matrix a la matrice identité champ par champ, tout ça pour la réaffecter a ta NSAffineTransform xform pour pouvoir repartir d'une matrice identité pour le reste du code qui l'a modifié, ça serait aussi simple de créer une autre variable NSAffineTransform gril scratch et de l'utiliser pour la fin du code.


    Par ce que là , c'est comme si après avoir fait un schéma sur un papier, tu gommais tout ce que tu avais écrit sur le papier, pour pouvoir repartir d'une feuille vierge,... au lieu de tout simplement prendre une nouvelle feuille vierge directement !




     


    Le problème est que justement, je ne veux pas de page blanche, mais continuer à  dessiner sur la précédente. Mon bout de code fonctionne de ce point de vue.


     


    En ce qui concerne la NSGraphicContext, il m'a semblé que cette classe traite des polices de caractères, des couleurs, des types de tracés, mais pas à  proprement parlé de transformation du plan. J'ai lu ceci :


    https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/GraphicsContexts/GraphicsContexts.html#//apple_ref/doc/uid/TP40003290-CH203-BCIJFBJJ


     


    Je reviendrai sur ce sujet plus tard sans doute. Là , je commence à  travailler sur mon algo d'IFS : je souhaite démultiplier une image dans une fractale!


     


    A suivre...


  • AliGatorAliGator Membre, Modérateur

    Le problème est que justement, je ne veux pas de page blanche, mais continuer à  dessiner sur la précédente. Mon bout de code fonctionne de ce point de vue.

    Bon mon allégorie n'était peut-être pas bien choisi en parlant de page en fait, mais mon image de la feuille de papier était à  comparer avec la matrice, pas avec la zone de dessin sur laquelle tu dessines (oui du coup le côté "feuille de papier" comme élément de comparaison porte à  confusion effectivement désolé)

    Ce que je voulais dire c'est que dans ton code, tu réutilises la variable "matrix" mais fait des pieds et des mains pour changer ses valeurs m11, m12, ... tout ça pour pouvoir la réutiliser en repartant d'une matrice identité, au lieu de directement en utiliser une autre. C'est comme si tu avais une variable "x" que tu avais utilisée (dans ton cas matrix), et que tu avais une méthode qui faisait "x += 30" (les méthodes "translateXBy:YBy:" & co que tu veux utiliser à  la fin) et du coup tu voulais absolument utiliser x et était obligé de faire un "x=0" avant de pouvoir utiliser "x += 30" pour qu'il valle 30 à  la fin... au lieu d'utiliser une autre variable "y" qui est déjà  initialisée à  0. Sauf que dans cet exemple x et y c'est que des entiers donc ça va c'est pas méchant de devoir faire un "x=0" plutôt que d'utiliser une nouvelle variable "y", mais dans le cas de ta "matrix", ça t'oblige à  remettre tous les 6 champs de la structure aux bonnes valeurs pour retomber sur la matrice identité... aucun intérêt.

    Donc non je persiste, ça serait bien plus simple d'utiliser une nouvelle variable NSAffineTransformStruct pou la fin de ton code que t'embêter à  modifier l'ancienne pour la remettre à  l'identité. (Ca ne changera pas que tu continuera à  dessiner sur l'image précédente, ça c'est autre chose, là  je te parle des variables que tu manipules pour créer tes matrices que tu vas appliquer à  ton contexte de dessin)

    En ce qui concerne la NSGraphicContext, il m'a semblé que cette classe traite des polices de caractères, des couleurs, des types de tracés, mais pas à  proprement parlé de transformation du plan. J'ai lu ceci :
    https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/GraphicsContexts/GraphicsContexts.html#//apple_ref/doc/uid/TP40003290-CH203-BCIJFBJJ

    NSGraphicContext manipule également la matrice de transformation courante. C'est d'ailleurs le contexte graphique qui est modifié quand tu appelles "[monAffineTransform concat]" : cela doit bien concaténer la matrice "monAffineTransform" à  quelque chose, et ce quelque chose, c'est la matrice de transformation courante du NSGraphicContext justement, qui garde à  tout moment en mémoire quel est la matrice/transformation actuellement appliquée pour l'appliquer sur les futures opérations de dessin faites sur ce contexte.
  • J'ai enfin compris ce que tu voulais dire. Des fois, cela prend du temps à  monter au cerveau!


     


    Effectivement, il est plus simple de crée une nouvelle NSAffineTransform que de réécrire l'ancienne...


     


    Par contre, la double définition du point d'origine demeure nécessaire.


     


    Je vais encore essayer de comprendre la classe NSGraphicContext. Logiquement en effet, elle devrait répondre à  mes besoins. Je cherche encore.


     


    Merci une nouvelle fois AliGator en tous les cas :)


Connectez-vous ou Inscrivez-vous pour répondre.