Vi ser linjer som används i många scenarier. Kurvor används också, men kanske inte lika ofta - men det undergräver inte deras betydelse! I denna handledning ska vi titta närmare på kurvor, särskilt den kvadratiska och kubiska kurvan, tillsammans med några av deras vanliga matematiska egenskaper.
Låt oss ta en titt på det slutliga resultatet vi kommer att arbeta för. Dra de röda prickarna och se förändringarna i positionen.
Och här är en annan demo, med kubiska kurvor, utan gradienterna:
Kvadratisk och kubisk kommer att presenteras i var och en av dessa sektioner. Så låt oss först titta på kurvens ekvation. Dessa ekvationer är skrivna i polynom form, med början med högsta graden. Den första är kvadratisk ekvation (högsta graden är 2); den andra är kubisk ekvation (högsta graden är 3).
\ [f (x) = Axe ^ 2 + Bx + C \ ... (eq \ 1) \]
\ [g (x) = Axe ^ 3 + Bx ^ 2 + Cx + D \ ... (eq \ 2) \]
Observera att A, B, C och D är reella tal. Så nu vi är bekanta med det, låt oss försöka visualisera det. Grafikkurvor kommer att bli vårt nästa försök.
Låt oss först styra en kvadratisk kurva. Jag är säker på att alla läsare har graderat kvadratisk kurva i gymnasiet, men för att uppdatera ditt minne presenterar jag diagram nedan. De placeras sida om sida för att underlätta jämförelsen.
Den uppenbara skillnaden är den inverterade y-axeln på Flash-koordinatutrymme. De ser enkla överallt, eller hur? Okej, nu är vi redo att plotta på Flash-koordinatutrymme.
För att placera kvadratiska kurvor på rätt plats måste vi förstå deras motsvarande ekvationer. Den här kurvan är verkligen beroende av ekvationens koefficienter (för kvadratiska fall är A, B och C).
Jag har inkluderat en Flash-presentation nedan så att du enkelt kan tweak dessa koefficienter och få omedelbar feedback.
För att studera effekterna av individuella koefficienter på den övergripande kurvan, föreslår jag att du följer nedanstående steg för att experimentera med Flash-presentationen ovan.
En annan intressant observation är att under hela det andra och tredje steget av ovanstående förblir böjningspunkten (dvs vändpunkten) vid samma punkt på y-axeln.
Du ser snabbt att positionering en kurva är något svårt. Den använda ekvationen är opraktisk om vi vill säga, hitta koordinaterna för den lägsta punkten på en kurva.
Lösning? Vi skriver om ekvationen i önskad form. Kolla in följande ekvation:
\ [f (x) = P (x + Q) ^ 2 + R \]
Det är fortfarande en kvadratisk ekvation, men det har tagits en annan form. Nu kan vi enkelt styra minsta och maximala punkter på kurvan. I den tidigare Flash-presentationen klickar du på knappen "Approach 1" längst upp till höger och spelar med de nya värdena.
Här är en kort förklaring av koefficienterna roller:
Koefficient | Roll |
P | Kontrollera kurvens branthet. |
Q | Styrförskjutning av kurvens vändpunkt längs x-axeln. |
R | Styrförskjutning av kurvens vändpunkt längs y-axeln. |
Ändå är det fortfarande en svår uppgift att få kurvan att passera genom en viss uppsättning poäng. Vi måste noggrant förberäkna på papper innan vi översätter den till kod.
Lyckligtvis finns det en bättre lösning. Men innan vi går igenom det, låt oss ta en titt på ActionScript-implementeringen från och med nu.
Här är ekvationerna skrivna som ActionScript-funktioner (kontrollera Graphing.as
i källkoden nedladdning).
Privat funktion kvadratisk1 (x: Nummer, A: Nummer, B: Nummer, C: Nummer): Nummer // y = A (x ^ 2) + B (x) + C retur A * x * x + B * x + C Privatfunktion kvadratisk2 (x: Nummer, P: Nummer, Q: Nummer, R: Nummer): Nummer // y = P * (x + Q) ^ 2 + R återvänd P * (x + Q) * x + Q) + R
Och här är en implementering av ritningsmetoden med Graphics.drawPath ()
. Bara en anteckning att alla kurvor i denna artikel är ritade på liknande sätt.
Först variablerna ...
privata var poäng: Vector.= ny vektor. ; privat var drawCommand: Vector. = ny vektor. ;
Nu y-positionerna, beräknade baserat på x-positionerna och de givna koefficienterna.
privata funktionen redraw (A: Nummer, B: Nummer, C: Nummer): void for (var i: int = 0; i < 400; i++) var x:Number = i - 200; points[i * 2] = x * 10 + stage.stageWidth >> 1; om (isApproach1) poäng [i * 2 + 1] = kvadratisk1 (x, A, B, C) + stadium.stageHeight >> 1 annat poäng [i * 2 + 1] = kvadratisk2 (x, A, B , C) + stadium.stageHeight >> 1 om (i == 0) drawCommand [i] = 1; annars drawCommand [i] = 2; graphics.clear (); Graphics.lineStyle (1); graphics.drawPath (drawCommand, points);
(Förvirrad om >>
operatör? Ta en titt på denna handledning.)
Antag att vi får tre punkter som den kvadratiska kurvan måste passera genom; hur bildar vi motsvarande ekvation? Mer specifikt, hur kan vi bestämma ekvationens koefficientvärden? Linjär algebra kommer till undsättning. Låt oss analysera detta problem.
Vi vet att kvadratiska ekvationer alltid tar form som skrivet i ekv. 1 i steg 1.
\ [f (x) = Axe ^ 2 + Bx + C \ ... (eq \ 1) \]
Eftersom alla tre koordinater ges ligger på samma kurva, måste de varje tillfredsställa denna ekvation med samma koefficienter som kurvans ekvation som vi letar efter. Låt oss skriva ner detta i ekvationsform.
Med tanke på tre coodinates:
Ersätt dessa värden i (eq 1). Observera att A, B, C är okända för tillfället.
\ [f (x) = Axe ^ 2 + Bx + C \ ... (eq \ 1) \]
Skriv nu om i matrisform. Notera att A, B, C är de okända vi löser.
[latex]
\ begin bmatrix S_y \\ T_y \\ U_y \ end bmatrix =
\ Begin bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix
\ begin bmatrix A \\ B \\ C \ end bmatrix \\
[/latex]
[latex]
\ Begin bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix ^ - 1
\ begin bmatrix S_y \\ T_y \\ U_y \ end bmatrix =
\ Begin bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix ^ - 1
\ Begin bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix
\ begin bmatrix A \\ B \\ C \ end bmatrix \\
[/latex]
[latex]
\ Begin bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix ^ - 1
\ begin bmatrix S_y \\ T_y \\ U_y \ end bmatrix
= I
\ begin bmatrix A \\ B \\ C \ end bmatrix
\\
K ^ - 1 J = L
[/latex]
Naturligtvis kan vi använda samtidiga ekvationer istället, men jag föredrar att använda matriser eftersom det är enklare. (Redaktörens anteckning: så länge du förstår matriser, det är det!)
Vi får inversen av K och multiplicerar med J-matrisen för att få L. Efter att vi framgångsrikt har löst för A, B, C kommer vi bara att ersätta den kvadratiska ekvationen. Således kommer vi att ha en kvadratisk kurva som passerar igenom alla tre punkterna.
Som nämnts i föregående steg måste vi utföra en 3x3 matrix inversion och multiplikation. Action s flash.geom.Matrix
klassen kommer inte att kunna hjälpa till med detta. Naturligtvis har vi val att använda flash.geom.Matrix3D
, klass men jag föredrar Coral-biblioteket eftersom jag kan pry i dessa anpassade klasser och undersöka vad som händer under huven. Jag tycker personligen att detta är mycket användbart när du är osäker på korrekt användning av kommandon även efter att du läst API-dokumentationen.
Så ladda ner och placera de oförsedda Coral-filerna i din projektkälla mapp.
Här är ett urval av resultatet. Försök att omplacera de röda prickarna och se den kvadratiska kurvan som repeteras för att passera igenom alla tre punkterna.
Du kan hitta hela skriptet i Draw_curve.as.
Följande ActionScript är bara att aktivera muskontroller på de små prickarna.
allmän funktion Draw_Curve () // inställning av kontroller c1 = ny cirkel (0xFF0000); addChild (c1); c1.x = stage.stageWidth * 0.2; c1.y = stage.stageHeight >> 1; c2 = ny cirkel (0xFF0000); addChild (c2); c2.x = stadium.stageWidth * 0,5; c2.y = stage.stageHeight >> 1; c3 = ny cirkel (0xFF0000); addChild (c3); c3.x = stadium.stageWidth * 0,8; c3.y = stage.stageHeight >> 1; c1.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c1.addEventListener (MouseEvent.MOUSE_UP, flytta); c2.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c2.addEventListener (MouseEvent.MOUSE_UP, flytta); c3.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c3.addEventListener (MouseEvent.MOUSE_UP, flytta); redraw () privatfunktionsflyttning (e: MouseEvent): void if (e.type == "mouseDown") e.target.startDrag () e.target.addEventListener (MouseEvent.MOUSE_MOVE, uppdatering); annars om (e.type == "mouseUp") e.target.stopDrag (); e.target.removeEventListener (MouseEvent.MOUSE_MOVE, uppdatering); privatfunktionsuppdatering (e: MouseEvent): void redraw ();
Kärnan ligger i rita om
fungera. Jag har markerat matrisoperationerna och den kvadratiska funktionen för redraw-processen.
privat funktion redraw (): void K = ny Matrix3d (c1.x * c1.x, c1.x, 1, 0, c2.x * c2.x, c2.x, 1, 0, c3.x * c3 .x, c3.x, 1, 0, 0, 0, 0, 1); K.invert () L = ny Matrix3d (c1.y, 0, 0, 0, c2.y, 0, 0, 0, c3.y, 0, 0, 0, 0, 0, 0, 0); L.append (K); graphics.clear (); var poäng: Vector.= ny vektor. ; var cmd: vektor. = ny vektor. ; för (var i: int = 0; i < 200; i++) //current x var x:Number = i * 2; //f(x) = A (x^2) + B (x) + C var y:Number = L.n11* x* x + L.n21 * x + L.n31 ; points.push(x, y); if (i == 0) cmd.push(1); else cmd.push(2); graphics.lineStyle(1); graphics.drawPath(cmd, points);
Så du kan se att matrisen K initierades och inverterades innan den fästes på matrisen J.
De bifoga()
funktion multiplicerar nuvarande matris, J, med ingångsmatrisen, K, placerad till vänster. En annan anmärkningsvärd detalj är att vi inte använder alla rader och kolumner i K och J matriser. Men eftersom matrixinversion endast kan hända med en fyrkantig matris, måste vi fylla i 4: e kolumnelementet i K med 1. (Det behöver inte göras för J eftersom vi inte behöver dess inversion i vår beräkning. ) Således kan du se att alla andra element är 0 utom för den första kolumnen.
Så det är allt för att teckna kvadratiska kurvor. Låt oss gå vidare till kubiska kurvor.
Återigen får vi en liten översyn av graferna för dessa kurvor. Kolla in följande bild:
När du jämför den här kurvan med kvadratisk, märker du att den är brantare, och att en del av kurvan ligger under x-axeln. En halv är speglad vertikalt, jämfört med en kvadratisk.
Jag har inkluderat följande Flash-presentation för att låta dig experimentera med koefficienterna i en kubisk ekvation. Försök att justera värdet på A från positivt till negativt och observera skillnaden i den producerade kurvan.
Här är den viktiga delen av genomförandet av diagrammet ovan:
privata funktionen redraw (A: Nummer, B: Nummer, C: Nummer, D: Nummer): void for (var i: int = 0; i < 400; i++) var x:Number = i - 200; points[i * 2] = x * 10 + stage.stageWidth >> 1; poäng [i * 2 + 1] = kubik1 (x, A, B, C, D) + stadium.stageHeight >> 1 om (i == 0) drawCommand [i] = 1; annars drawCommand [i] = 2; graphics.clear (); Graphics.lineStyle (1); graphics.drawPath (drawCommand, points); Privat funktion cubic1 (x: Nummer, A: Nummer, B: Nummer, C: Nummer, D: Nummer): Nummer // y = A (x ^ 3) + B (x ^ 2) + C + D returnera A * x * x * x + b * x * x + c * x + d
Återigen är det svårt att placera den kubiska kurvan enligt en uppsättning punkter som den passerar igenom. Återigen hänvisar vi till linjär algebra för ett alternativ.
Vi vet från steg 6 att koefficienterna för en kvadratisk ekvation kan beräknas baserat på tre givna punkter och kurvan som dras från den kommer att passera genom dessa punkter. Ett liknande tillvägagångssätt kan utföras med någon fyra givna poäng för att erhålla en kubisk ekvation:
Ersätt dessa koordinater i (ekv 2). Observera att A, B, C, D är okända.
\ [g (x) = Axe ^ 3 + Bx ^ 2 + Cx + D \ ... (eq \ 2) \]
Men nu ska vi hantera en 4x4-matris istället för 3x3-matrisen:
\ (
\ begin bmatrix S_y \\ T_y \\ U_y \\ V_y \ end bmatrix =
\ Begin bmatrix
\ left (S_x \ right) ^ 3 & \ left (S_x \ right) ^ 2 & \ left (S_x \ right) & 1 \\
\ left (T_x \ right) ^ 3 & \ left (T_x \ right) ^ 2 & \ left (T_x \ right) & 1 \\
\ left (U_x \ right) ^ 3 & \ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \\
\ left (V_x \ right) ^ 3 & \ left (V_x \ right) ^ 2 & \ left (V_x \ right) & 1 \ end bmatrix
\ begin bmatrix A \\ B \\ C \\ D \ end bmatrix \\
P = QR \\
Q ^ - 1 P = Q ^ - 1 QR \\
Q ^ - 1 P = IR \\
Q ^ - 1 P = R
\)
Nu kommer vi att använda alla element i 4x4-matrisen för Q och hela första kolumnen för P. Då är Q inverterad och applicerad på P.
Återigen ställer vi upp musknapparna för att tillåta att dra dessa punkter. När någon av dessa punkter dras, sker omräkning och omräkning av kurvan ständigt.
allmän funktion Draw_Curve2 () // inställning av kontroller c1 = ny cirkel (0xFF0000); addChild (c1); c1.x = stage.stageWidth * 0.2; c1.y = stage.stageHeight >> 1; c2 = ny cirkel (0xFF0000); addChild (c2); c2.x = stadium.stageWidth * 0,4; c2.y = stage.stageHeight >> 1; c3 = ny cirkel (0xFF0000); addChild (c3); c3.x = stage.stageWidth * 0,6; c3.y = stage.stageHeight >> 1; c4 = ny cirkel (0xFF0000); addChild (c4); c4.x = stadium.stageWidth * 0,8; c4.y = stage.stageHeight >> 1; c1.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c1.addEventListener (MouseEvent.MOUSE_UP, flytta); c2.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c2.addEventListener (MouseEvent.MOUSE_UP, flytta); c3.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c3.addEventListener (MouseEvent.MOUSE_UP, flytta); c4.addEventListener (MouseEvent.MOUSE_DOWN, flytta); c4.addEventListener (MouseEvent.MOUSE_UP, flytta); rita om(); Private Function Move (e: MouseEvent): void if (e.type == "mouseDown") e.target.startDrag () e.target.addEventListener (MouseEvent.MOUSE_MOVE, uppdatering); annars om (e.type == "mouseUp") e.target.stopDrag (); e.target.removeEventListener (MouseEvent.MOUSE_MOVE, uppdatering); privatfunktionsuppdatering (e: MouseEvent): void redraw ();
rita om
är den avgörande funktionen där allt hände.
privat funktion redraw (): void var vänster: Matrix3d = ny Matrix3d (c1.x * c1.x * c1.x, c1.x * c1.x, c1.x, 1, c2.x * c2.x * c2.x, c2.x * c2.x, c2.x, 1, c3.x * c3.x * c3.x, c3.x * c3.x, c3.x, 1, c4.x * c4. x * c4.x, c4.x * c4.x, c4.x, 1); left.invert () var rätt: Matrix3d = ny Matrix3d (c1.y, 0, 0, 0, c2.y, 0, 0, 0, c3.y, 0, 0, 0, c4.y, 0, 0 , 0); right.append (till vänster); // f (x) = A (x ^ 3) + B (x ^ 2) + C (x) + D graphics.clear (); var poäng: Vector.= ny vektor. ; var cmd: vektor. = ny vektor. ; för (var i: int = 0; i < 200; i++) var x:Number = i * 2; var y:Number = right.n11 * x * x * x+ right.n21 * x * x+ right.n31 * x + right.n41; points.push(x, y); if (i == 0) cmd.push(1); else cmd.push(2); graphics.lineStyle(1); graphics.drawPath(cmd, points);
Slutligen, låt oss titta på produkten. Klicka och flytta de röda prickarna för att se kubisk kurva ritad för att passera genom alla dessa punkter.
Vi har just gått igenom polynomier av grad 2 och 3 (kvadratisk och kubisk). Från vår erfarenhet kan vi förutsäga att beräkning för polynom av grader 4 (quintic) kommer att kräva fem punkter, vilket kräver 5x5 matris och så vidare för polynom med ännu högre grader.
Tyvärr, Korall
och flash.geom.Matrix3D
tillåta bara 4x4 matriser, så du ska skriva din egen klass om behovet kommer. Det krävs sällan i spel.
Låt oss försöka tillämpa vår kunskap för att dela upp regioner på vårt stadium. Detta kräver en viss omprövning av ekvationernas ekvation. Kolla in bilden nedan.
Denna bild ovan visar en kurva som delar regionerna i två:
Det är inte svårt att förstå detta koncept. I själva verket har du redan experimenterat med detta i steg 11 när du tweaked koefficienterna för den kubiska formeln. Föreställ dig, i koordinatsystemet, att det finns ett oändligt antal kurvor, alla differentierade av bara en liten förändring i D:
Så här är provet av utgång för kvadratisk kurva. Du kan försöka flytta den röda pricken runt och se regionerna färgade.
Här är det viktiga ActionScript-fragmentet. Kolla in hela skriptet i Region_Curve.as
privat funktion redraw (): void var vänster: Matrix3d = ny Matrix3d (c1.x * c1.x, c1.x, 1, 0, c2.x * c2.x, c2.x, 1, 0, c3. x * c3.x, c3.x, 1, 0, 0, 0, 0, 1); left.invert () var rätt: Matrix3d = ny Matrix3d (c1.y, 0, 0, 0, c2.y, 0, 0, 0, c3.y, 0, 0, 0, 0, 0, 0, 0 ); right.append (till vänster); // Var = A (x ^ 2) + B (x) + C för varje (var-artikel: Cirkel i bakgrunden) var D: Nummer = right.n11 * item.x * item.x + right.n21 * item .x + right.n31; //trace(background[i].y); om (item.y> D) item.color = 0; annars item.color = 0xAAAAAA;
Här är provet med avseende på kubisk kurva.
Och genomförandet som följer med det. Återigen, hela skriptet är in Region_Curve2.as
// Var = A + B (x) + C (x ^ 2) för var och en (varartikel: Cirkel i bakgrunden) var D: Nummer = right.n11 * item.x * item.x * item.x; + right.n21 * item.x * item.x + right.n31 * item.x + right.n41 //trace(background[i].y); om (item.y> D) item.color = 0; annars item.color = 0xAAAAAA;
Vad sägs om några tweaks för att ändra färgen över olika kurvor? Återigen musklickar du på de röda prickarna och ser gradientförändringarna över skärmen.
Här är det viktiga ActionScript-fragmentet som extraheras från Region_Curve3.as
. Först och främst vill vi ta reda på max och minsta offset från originalkurvan.
var max: nummer = 0; var min: Nummer = 0; var Ds: Vector.= ny vektor. ; // Var = A (x ^ 2) + B (x) + C för varje (var-artikel: Cirkel i bakgrunden) var D: Nummer = right.n11 * item.x * item.x + right.n21 * item .x + right.n31; var offset: Number = item.y - D; Ds.push (offset); om (item.y> D && offset> max) max = offset; annars om (item.y < D && offset < min) min = offset;
När vi gjort det, applicerar vi det för att färga de enskilda prickarna.
// färgvariationer baserade på förskjutningen var färg: Nummer för (var i: int = 0; i < background.length; i++) if (Ds[i] > 0) färg = Ds [i] / max * 255 // beräkna färg till plats i bakgrunden [i]. Färg = färg<<16 | color<<8 | color; //define a grayscale else if (Ds[i] < 0) color = Ds[i] / min * 255; background[i].color = color<<16; //define a gradient of red
Så att allt för ritning av kurvor. Nästa upp, hitta rötter av en kvadratisk och kubisk kurva. Tack för att du läser. Dela om du ser några verkliga applikationer som utnyttjar denna handledning.