Calcul d'un "endPoint" en fonction d'un angle

octobre 2012 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,



Je crée ce topic car je suis un peu perdu dans mes formules mathématiques..

Dans le but de créer un wrapper Objective-C pour CGGradient (pour iOS, NSGradient n'étant pas dispo pour cette plateforme).

Je me base donc entièrement sur NSGradient pour essayer de re-créer à  l'identique mon wrapper CGGradient.

La méthode -drawInRect:angle: me pose problème, puisque CGGradient requiert un startPoint et un endPoint.



Ci dessous, un tableau (issu de NSGradient) qui liste les 4 startPoint possibles selon l'angle:


0-89° : Lower-left (minX,maxY)

90-179° : Lower-right (maxX,maxY)

180-269° : Upper-right (maxX, minY)

270-359° : Upper-left (minX,minY)




Si on prend donc un angle à  90°, mon gradient aura un startPoint de maxX(rect), maxY(rect), et un endPoint de maxX(rect), minY(rect).

Pour un angle à  0 ou 360°, mon gradient aura un startPoint de minX(rect), maxY(rect), et un endPoint de maxX(rect), maxY(rect).



Voilà  pour les deux exemples. Le startPoint est donc facile à  déterminer puisqu'il suffit de suivre le tableau.

J'ai donc procédé comme suit:


<br />
<br />
- (CGFloat)__positiveAngleFromAngle:(CGFloat)angle<br />
{<br />
    return (CGFloat)((360+((int)(round(angle)) % 360))%360);<br />
}<br />
<br />
- (CGPoint)__startLocationForAngle:(CGFloat)angle inRect:(CGRect)rect<br />
{<br />
    CGPoint startLocation = CGPointZero;<br />
    CGFloat positiveAngle = [self __positiveAngleFromAngle:angle];<br />
<br />
    if (positiveAngle &lt; 90.0f)<br />
        startLocation = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));<br />
    else if (positiveAngle &lt; 180.0f)<br />
        startLocation = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));<br />
    else if (positiveAngle &lt; 270.0f)<br />
        startLocation = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));<br />
    else<br />
        startLocation = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));<br />
<br />
    return startLocation;<br />
}<br />




Je ne suis pas trop fan de ces conditions, mais je ne vois pas d'autre solution pour l'instant.



Le gros problème pour moi c'est le calcul du endPoint. Ma première erreur aura été de me baser sur un carrée pour essayer de trouver une formule adéquate... Il vaut mieux partir sur un rectangle.. 6x4 par exemple..



Quelques exemples simples pour le endPoint à  des valeurs précises pour une forme carrée:

0° : Starts from lower-left, Ends to lower-right

45° : Starts from lower-left, Ends to upper-right

90° : Starts from lower-right, Ends to upper-right



Je ne sais pas quoi indiquer de plus.. J'espère simplement que vous avez cerné mon problème, et surtout le but..

Réponses

  • AliGatorAliGator Membre, Modérateur
    Tu veux que ton endPoint soit forcément à  une des extrémités (c'est à  dire tombe sur le contour de ton rect) ou pas forcément ?



    Quand tu fais un gradient par exemple horizontal, qui va de gauche à  droite, tu peux mettre le startPoint tout à  gauche de ton rect, et le endPoint soit tout à  droite... soit à  droite du startPoint, mais sur le chemin qui va jusqu'au bord droit, et pas forcément tout au bout. Le vecteur [startPoint ... endPoint] définit à  la fois la direction du gradient (selon la direction du segment) mais aussi son étalement (selon la longueur du vecteur, donc la distance entre startPoint et endPoint). Plus tes 2 points seront éloignés, plus le gradient sera étiré.

    C'est ce qui se passe sous Photoshop par exemple quand tu prend l'outil gradient, tu cliques pour positionner le startPoint, et ensuite quand tu drag ta souris dans une direction données, tu étires le gradient plus tu t'éloignes de ton point de départ.



    Après peut-être que tu veux que ton endPoint soit le point à  l'intérieur de ton rect qui soit le plus loin possible de ton startPoint -- pour étirer au maximum ton gradient tout en étant sûr de voir toutes les couleurs du panel du gradient (y compris la endColor quoi) -- mais dans la direction définie par ton angle demandé ? Ou pas ?



    Il faut que tu nous précise exactement ce que tu veux pour ce endPoint
  • octobre 2012 modifié #3
    Le endPoint doit se trouver sur le tracé du rect.

    Pour un rect {0,0,6,4} et un angle de 45°.. le startPoint se trouve donc à  {minX, maxY} mais le endPoint se trouve à  {4,0}

    Si ça avait été un carré {0,0,6,6}, par exemple, toujours avec un angle de 45°, le endPoint se serait trouvé à  {6,0}



    De plus, je n'ai pas la main sur la valeur de l'angle.. On pourrait très bien passer 75°..
  • CéroceCéroce Membre, Modérateur
    Le diagramme de gauche me semble faux. Il faut imaginer que la droite passe par le centre du rectangle.
  • AliGatorAliGator Membre, Modérateur
    Si tu veux le point se trouvant sur ton rect, voilà  une idée d'algo :



    L'idée est de trouver l'intersection entre la demi-droite D qui part de startPoint et va dans la direction spécifiée par ton angle, et ton rectangle. Cette intersection va se placer soit sur le bord vertical opposé à  ton startPoint, soit sur le bord horizontal.



    Donc du coup l'idée est de calculer l'intersection de ta demi-droite D avec chacun des 2 bords opposés, pour trouver les intersections E1 et E2 sur ma capture. Et de retenir le point entre E1 et E2 qui est le plus proche. Dans mon exemple ci-dessous, si ton angle est petit, c'est E1 (intersection avec le bord vertical opposé) qui sera le plus proche et donc retenu, si ton angle est plus grand c'est E2 qui sera plus proche de S.



    Dans l'idée ça donnerait un truc du genre :
    double t = tan(angle)<br />
    CGPoint endPoint;<br />
    if (fabs(t) &lt; rect.height/rect.width) {<br />
      endPoint = CGPointMake(startPoint.x + rect.width , startPoint.y - t*rect.width)<br />
    } else {<br />
      endPoint = CGPointMake(startPoint.x + t*rect.height , startPoint.y - rect.height)<br />
    }
    
  • LarmeLarme Membre
    octobre 2012 modifié #6
    J'aurais aimé aider (car j'ai déjà  été confronté à  quelques problèmes mathématiques de ce genre, avec un slider courbe par exemple), mais là , ça commence à  devenir compliqué un mercredi soir image/ohmy.png' class='bbc_emoticon' alt=':o' />
  • 'Céroce' a écrit:
    Le diagramme de gauche me semble faux. Il faut imaginer que la droite passe par le centre du rectangle.


    Non il est bon.. L'angle est bien de 45°
  • 'Larme' a écrit:


    J'aurais aimé aider (car j'ai déjà  été confronté à  quelques problèmes mathématiques de ce genre, avec un slider courbe par exemple), mais là , ça commence à  devenir compliqué un mercredi soir image/ohmy.png' class='bbc_emoticon' alt=':o' />




    surtout que l'on était jeudi image/biggrin.png' class='bbc_emoticon' alt=':D' />
  • CéroceCéroce Membre, Modérateur
    J'ai fait un programme d'essai:



  • CéroceCéroce Membre, Modérateur
    octobre 2012 modifié #10
    Cette implémentation interpole entre 8 points placés tous les 45 degrés:



  • Ton premier exemple m'a l'air plus plausible, excepté que ça ne démarre donc pas à  des points précis comme le précise la doc Apple, mais très honnêtement ton 1er exemple me semble bien se rapprocher d'un fonctionnement standard.

    Merci !
  • CéroceCéroce Membre, Modérateur
    octobre 2012 modifié #12
    Les deux exemples correspondent avec ce qui est écrit dans la doc. Je ne sais pas si tu as vu, mais le dégradé est fait par NSGradient, c'est donc lui qui fait foi.



    Le premier exemple est ce à  quoi je m'attendais, mais vraiment, le second exemple est plus proche du résultat du NSGradient. Son implémentation est également plus simple, et comme je pense qu'Apple est allé au plus simple, je ne dois pas être très loin ;-)
  • 'Céroce' a écrit:


    Les deux exemples correspondent avec ce qui est écrit dans la doc. Je ne sais pas si tu as vu, mais le dégradé est fait par NSGradient, c'est donc lui qui fait foi.



    Le premier exemple est ce à  quoi je m'attendais, mais vraiment, le second exemple est plus proche du résultat du NSGradient. Son implémentation est également plus simple, et comme je pense qu'Apple est allé au plus simple, je ne dois pas être très loin ;-)




    Cool, allez encore1599 classes a faire et hop on a OSX en open source image/smile.png' class='bbc_emoticon' alt=':)' />



    Peut-être sur le site cocotron.org, il ont fait la classe?
  • CéroceCéroce Membre, Modérateur
    octobre 2012 modifié #14
    'devulder' a écrit:


    Peut-être sur le site cocotron.org, il ont fait la classe?


    Je n'y avais même pas songé. Oui, elle y est!



    Et le schéma qui donne des explications.
Connectez-vous ou Inscrivez-vous pour répondre.