Maths et ActionScript des courbes Dégradés et Normales

Nous avons abordé le dessin des courbes et découvert leurs racines quadratiques et cubiques, ainsi que des applications pratiques pour l’utilisation des racines quadratiques dans les jeux. Maintenant, comme promis, nous allons examiner les applications pour trouver cubique racines, ainsi que les gradients et les normales des courbes, tels que faire rebondir des objets sur des surfaces courbes. Allons-y!


Exemple

Jetons un coup d'œil à une utilisation pratique de ce calcul:

Dans cette démonstration, le "navire" rebondit sur les bords du fichier SWF et de la courbe. Le point jaune représente le point le plus proche du navire qui se trouve sur la courbe. Vous pouvez ajuster la forme de la courbe en faisant glisser les points rouges et ajuster le mouvement du navire à l'aide des touches fléchées.


Étape 1: Distance la plus courte d'une courbe

Considérons le scénario où un point est situé près d'une courbe quadratique. Comment calculer la distance la plus courte entre le point et la courbe?

Eh bien, commençons par le théorème de Pythagore.

\ [
Soit \ le \ point \ be \ (x_p, \ y_p) \\
et \ appelez \ le \ point le plus proche \ sur \ la \ courbe \ (x_c, \ y_c) \\
Ensuite:\\
z ^ 2 = x ^ 2 + y ^ 2 \\
z ^ 2 = (x_c-x_p) ^ 2 + (y_c-y_p) ^ 2 \\
Étant donné \ y_c = ax_c ^ 2 + bx_c + c, \\
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]

Vous pouvez voir que nous avons remplacé \ (y_c \) par l'équation du second degré. En bref, nous pouvons voir que la puissance la plus élevée est 4. Ainsi, nous avons un quart équation. Tout ce que nous avons à faire est de trouver un point minimum dans cette équation pour obtenir la distance la plus courte entre un point et une courbe quadratique.

Mais avant cela, nous devrons comprendre les gradients sur une courbe…


Étape 2: Gradient d'une courbe

Avant d'examiner le problème de la minimisation d'une équation quartique, essayons de comprendre les gradients d'une courbe. Une ligne droite n'a qu'un seul dégradé. Mais le gradient d'une courbe quadratique dépend du point de la courbe auquel nous nous référons. Découvrez la présentation Flash ci-dessous:

Faites glisser les points rouges pour modifier la courbe quadratique. Vous pouvez également jouer avec la poignée du curseur pour changer la position du point bleu le long de x. Lorsque le point bleu change, le dégradé dessiné.


Étape 3: Gradient Through Calculus

C'est ici que le calcul sera utile. Vous avez peut-être deviné que la différenciation d'une équation quadratique vous donnerait le gradient de la courbe.

\ [
f (x) = ax ^ 2 + bx + c \\
\ frac df (x) dx = 2ax + b
\]

Donc \ (\ frac df (x) dx \) est le dégradé d’une courbe du second degré et dépend de la coordonnée \ (x \). Eh bien, heureusement que nous avons une méthode pour gérer cela: diff1 (x: nombre) renverra la valeur après une seule différenciation.

Pour dessiner le dégradé, nous avons besoin d’une équation représentant la droite, \ (y = mx + c \). La coordonnée du point bleu \ ((x_p, y_p) \) sera substituée dans les \ (x \) et \ (y \), et le dégradé de la ligne trouvée par différenciation ira dans \ (m \). Ainsi, l'ordonnée à l'origine de la ligne, \ (c \) peut être calculée par un travail d'algèbre.

Découvrez l'AS3:

 var x: nombre = s.valeur var y: nombre = équation_ quadratique.fx_of (s.valeur) point.x = x; point.y = y; / ** * y = mx + c; * c = y - mx; <== use this to find c */ var m:Number = quadratic_equation.diff1(x); var c:Number = y - m * x; graphics.clear(); graphics.lineStyle(1, 0xff0000); graphics.moveTo(0, c); graphics.lineTo(stage.stageWidth, m * stage.stageWidth + c);

Étape 4: Systèmes de coordonnées

Tenez toujours compte de l'axe y inversé de l'espace de coordonnées Flash, comme indiqué dans l'image ci-dessous. À première vue, le diagramme de droite peut sembler être un gradient négatif, mais en raison de l'axe des y inversé, il s'agit en fait d'un gradient positif..

Il en va de même pour le point minimum indiqué ci-dessous. En raison de l'axe y inversé, le point minimum dans l'espace de coordonnées cartésiennes (en (0,0)) ressemble à un maximum dans l'espace de coordonnées Flash. Mais en se référant à la position d'origine dans l'espace de coordonnées Flash par rapport à la courbe quadratique, il s'agit en réalité d'un point minimum..


Étape 5: Taux de changement pour le gradient

Maintenant, supposons que je suis intéressé à trouver le point le plus bas d'une courbe - comment procéder? Regardez l'image ci-dessous (les deux figures sont dans le même espace de coordonnées).

Afin d'obtenir le point minimum, nous allons simplement assimiler \ (\ frac df (x) dx = 0 \), car par définition nous cherchons le point où le dégradé est zéro. Mais comme indiqué ci-dessus, il s’avère que le point maximum d’une courbe satisfait également à cette condition. Alors, comment pouvons-nous faire la distinction entre ces deux cas?

Essayons de différencier le deuxième degré. Ça va nous donner le taux de changement du gradient.

\ [
\ frac df (x) dx = 2ax + b \\
\ frac df ^ 2 (x) dx ^ 2 = 2a
\]

Je vais expliquer en référence à l'image ci-dessous (dessinée dans un espace de coordonnées cartésiennes). Nous pouvons voir que, lorsque nous incrémentons le long de l'axe des x, le gradient passe de négatif à positif. Donc, le taux de changement devrait être un positif valeur.

Nous pouvons aussi voir que lorsque \ (\ frac df ^ 2 (x) dx ^ 2 \) est positif, il y a un point minimum sur la courbe. Inversement si le taux est négatif, un point maximum est présent.


Étape 6: Retour au problème

Nous sommes maintenant prêts à résoudre le problème présenté à l’étape 1. Rappelons l’équation quartique (où le degré le plus élevé est 4) à laquelle nous sommes arrivés:

\ [
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]

La même équation quartique, tracée

N'oubliez pas que nous sommes intéressés à trouver le point minimum sur cette courbe, car le point correspondant sur la courbe quadratique d'origine sera le point situé à la distance minimale du point rouge..

Alors, différencions la fonction quartique pour obtenir le gradient de cette courbe, puis comparons le gradient de cette fonction quartique à zéro. Vous verrez que le dégradé est en fait une fonction cubique. Je renverrai les lecteurs intéressés à la page de Wolfram; pour ce tutoriel, je vais simplement cueillir le résultat de leurs travaux d'algèbre:

\ [
\ frac d (z ^ 2) dx =
2 (x_c-x_p) + 2 (ax_c ^ 2 + bx_c + c - y_p) (2ax_c + b) \\
\ frac d (z ^ 2) dx = 2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p- x_p) \\
Equate \ gradient \ to \ 0 \\
\ frac d (z ^ 2) dx = 0 \\
2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p-x_p) = 0 \\
Compare \ with \ cubic \ equation \\
Axe ^ 3 + Bx ^ 2 + Cx + D = 0 \\
A = 2a ^ 2 \\
B = 3ab \\
C = b ^ 2 + 2ac-2ay_p + 1 \\
D = bc-by_p-x_p
\]

Résolvez pour les racines de cette fonction cubique (plutôt compliquée) et nous arriverons aux coordonnées des trois points bleus comme indiqué ci-dessus.

Ensuite, comment filtrons-nous nos résultats pour le point minimum? Rappelez-vous de l'étape précédente qu'un point minimum a un taux de changement positif. Pour obtenir ce taux de changement, différenciez la fonction cubique qui représente le gradient. Si le taux de changement pour le point bleu donné est positif, il est un des les points minimum. Obtenir la point minimum, celui qui nous intéresse, choisissez le point avec le taux de changement le plus élevé.


Étape 7: Exemple de sortie

Alors, voici un exemple de mise en œuvre de l'idée expliquée ci-dessus. Vous pouvez faire glisser les points rouges pour personnaliser votre courbe quadratique. Le point bleu peut également être déplacé. Lorsque vous déplacez le point bleu, le point jaune sera repositionné de sorte que la distance entre les points bleu et jaune soit minimale entre tous les points de la courbe..

Lorsque vous interagissez avec la présentation Flash, il peut arriver que trois points jaunes apparaissent tous en même temps. Deux de celles-ci, estompées, font référence aux racines obtenues par le calcul mais rejetées car elles ne sont pas les points les plus proches de la courbe par rapport au point bleu..


Étape 8: Implémentation d'ActionScript

Alors, voici l'implémentation ActionScript de ce qui précède. Vous pouvez trouver le script complet dans Demo2.as.

Tout d'abord, nous devrons dessiner la courbe quadratique. Notez que la matrice m2 on se référera à d'autres calculs.

 fonction privée redraw_quadratic_curve (): void var cmd: Vector. = nouveau vecteur.; var coord: vecteur. = nouveau vecteur.; // redessiner la courbe; m1 = nouveau Matrix3d ​​(curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, 0, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x , 1, 0, points_courbe [2] .x * points_courbe [2] .x, points_courbe [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d ​​(points_courbe [0] .y, 0, 0, 0, points_courbe [1] .y, 0, 0, 0, points_courbe [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); pour (var i: int = 0; i < stage.stageWidth; i+=2)  if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i));  graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); 

Et voici celui qui implémente le concept mathématique expliqué. c1 fait référence à un point placé aléatoirement sur scène.

 fonction privée recalculate_distance (): void var a: Number = m2.n11; var b: nombre = m2.n21; var c: nombre = m2.n31; / * f (x) = Axe ^ 3 + Bx ^ 2 + Cx + D * / var A: Nombre = 2 * a * a var B: Nombre = 3 * b * a var C: Nombre = b * b + 2 * c * a - 2 * a * c1.y +1 var D: Nombre = c * b - b * c1.y - c1.x quartic_gradient = new EqCubic (); quartic_gradient.define (A, B, C, D); quartic_gradient.calcRoots (); roots = quartic_gradient.roots_R; var choisi: Nombre = racines [0]; if (! isNaN (racines [1]) &&! isNaN (racines [2])) // calcule le gradient et le taux de gradient de toutes les racines réelles var quartic_rate: Vector. = nouveau vecteur.; pour (var i: int = 0; i < roots.length; i++)  if (!isNaN(roots[i])) quartic_rate.push(quartic_gradient.diff1(roots[i])); else roots.splice(i, 1);  //select the root that will produce the shortest distance for (var j:int = 1; j < roots.length; j++)  //the rate that corresponds with the root must be the highest positive value //because that will correspond with the minimum point if (quartic_rate[j] > taux quartic_rate [j - 1]) choisi = racines [j];  // positionne les racines supplémentaires dans la démo position_extras ();  else // supprime les racines supplémentaires de la démo kill_extras ();  intersec_points [0] .x = choisi intersec_points [0] .y = équation_ quadratique.fx_of (choisi); 

Étape 9: Exemple: détection de collision

Utilisons ce concept pour détecter le chevauchement entre un cercle et une courbe.

L'idée est simple: si la distance entre le point bleu et le point jaune est inférieure au rayon du point bleu, nous avons une collision. Découvrez la démo ci-dessous. Les éléments interactifs sont les points rouges (pour contrôler la courbe) et le point bleu. Si le point bleu entre en collision avec la courbe, il disparaîtra un peu.


Étape 10: Implémentation d'ActionScript

Eh bien, le code est assez simple. Découvrez la source complète dans CollisionDetection.as.

 graphics.moveTo (intersec_points [0] .x, intersec_points [0] .y); graphics.lineTo (c1.x, c1.y); var distance: Number = Math2.Pythagoras (intersec_points [0] .x, intersec_points [0] .y, c1.x, c1.y) si (distance < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);

Étape 11: rebondir sur la courbe

Alors, maintenant que nous savons à quel moment une collision se produira, essayons de programmer une réponse. Que diriez-vous de rebondir sur la surface? Découvrez la présentation Flash ci-dessous.

Vous pouvez voir le navire (forme de triangle), est entouré d'un cercle (bleu translucide). Une fois que le cercle entre en collision avec la courbe, le navire rebondit sur la surface.


Étape 12: Contrôle du navire

Voici le code ActionScript pour contrôler le vaisseau.

 fonction publique CollisionDetection2 () / ** * Instanciation du navire et de sa zone circulaire bleue * / ship = new Triangle (); addChild (navire); ship.x = Math.random () * stage.stageWidth; ship.y = stage.stageHeight * 0.8; c1 = nouveau cercle (0x0000ff, 15); addChild (c1); c1al = 0,2; / ** * Vitesse du navire * / velo = nouveau Vector2D (0, -1); updateShip (); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener (KeyboardEvent.KEY_UP, handleKey); stage.addEventListener (Event.EXIT_FRAME, handleEnterFrame); / ** * La courbe et les calculs * / quadratic_equation = new EqQuadratic (); curve_points = nouveau vecteur.; peupler (curve_points, 0xff0000, 3); intersec_points = nouveau vecteur.; populate (intersec_points, 0xffff00, 3, false); redraw_quadratic_curve ();  fonction privée keyKey (e: KeyboardEvent): void if (type.e == "keyDown") si (code.key = = Keyboard.UP) est Up = true; else if (e.keyCode == Keyboard.DOWN) isDown = true; if (e.keyCode == Keyboard.LEFT) isLeft = true; else if (e.keyCode == Keyboard.RIGHT) isRight = true;  if (e.type == "keyUp") if (e.keyCode == Keyboard.UP) isUp = false; else if (e.keyCode == Keyboard.DOWN) isDown = false; if (e.keyCode == Keyboard.LEFT) isLeft = false; else if (e.keyCode == Keyboard.RIGHT) isRight = false;  fonction privée handleEnterFrame (e: Event): void / ** * Contrôle la magnitude * / if (isUp) velo.setMagnitude (Math.min (velo.getMagnitude () + 0.2, 3)); else if (isDown) velo.setMagnitude (Math.max (velo.getMagnitude () - 0,2, 1)); / ** * Contrôle la direction * / if (isRight) velo.setAngle (velo.getAngle () + 0.03); else if (isLeft) velo.setAngle (velo.getAngle () - 0,03); recalculate_distance (); si (distance < c1.radius) bounce(); updateShip();  /** * Update ship's position, orientation and it's area (the blue-ish circle) */ private function updateShip():void  ship.x += velo.x; ship.y += velo.y; ship.rotation = Math2.degreeOf(velo.getAngle()); c1.x = ship.x; c1.y = ship.y; if (ship.x > stage.stageWidth || ship.x < 0) velo.x *= -1; if (ship.y > stage.stageHeight || ship.y < 0) velo.y *= -1; 

Vous pouvez voir que les commandes du clavier mettent à jour les indicateurs pour indiquer si les touches gauche, haut, droite ou bas sont enfoncées. Ces drapeaux seront capturés par le gestionnaire d'événements d'entreprise et mettront à jour la magnitude et la direction du navire..


Étape 13: Calcul du vecteur de réflexion

J'ai déjà abordé le calcul vectoriel du vecteur de réflexion dans cet article. Ici, je vais juste expliquer comment obtenir le vecteur normal de gradient.

\ [
\ frac df (x) dx = dégradé \\
ligne \ gradient = \ frac y x \\
Supposer que \ gradient = 0.5 \\
y = 0.5 \\
x = 1 \\
Vecteur \ de \ left \ normal =
\ begin bmatrix -1 \\ 0.5 \ end bmatrix \\
Vecteur \ de \ right \ normal =
\ begin bmatrix 1 \\ - 0.5 \ end bmatrix
\]


Étape 14: Implémentation d'ActionScript

Ainsi, le code ActionScript ci-dessous implémentera le concept mathématique expliqué à l'étape précédente. Découvrez les lignes en surbrillance:

 fonction privée bounce (): void var gradient: Number = équation_ quadratique.diff1 (intersec_points [0] .x); var grad_vec: Vector2D = new Vector2D (1, dégradé); var left_norm: Vector2D = grad_vec.getNormal (false); var right_norm: Vector2D = grad_vec.getNormal (); var selected_vec: Vector2D; if (velo.dotProduct (left_norm)> 0) choisi_vec = gauche_norm sinon choisi_vec = droite_norm var choisi_unité: Vector2D = choisi_vec.normalise (); var proj: Number = velo.dotProduct (selected_unit); selected_unit.scale (-2 * proj); velo = velo.add (selected_unit); 

Conclusion

Eh bien, merci pour votre temps! Si vous avez trouvé cela utile ou si vous avez des questions, laissez des commentaires..