Vi ser linjer som brukes i mange scenarier. Kurver brukes også, men kanskje ikke så ofte - men det undergraver ikke deres betydning! I denne opplæringen skal vi se nærmere på kurver, særlig den kvadratiske og kubiske kurven, sammen med noen av deres vanlige matematiske egenskaper.
La oss se på det endelige resultatet vi skal jobbe for. Dra de røde prikkene og se endringer i posisjonen.
Og her er en annen demo, med kubiske kurver, uten gradienter:
Kvadratisk og kubisk vil bli omtalt i hver av disse seksjonene. Så la oss først se på kurvens ligning. Disse ligningene er skrevet i polynomisk form, begynner med termen av høyeste grad. Den første er kvadratisk ligning (høyeste grad er 2); den andre er kubisk ligning (høyeste grad er 3).
\ [f (x) = Aks ^ 2 + Bx + C \ ... (eq \ 1) \]
\ [g (x) = Aks ^ 3 + Bx ^ 2 + Cx + D \ ... (eq \ 2) \]
Merk at A, B, C og D er reelle tall. Så nå som vi er kjent med det, la oss prøve å visualisere det. Grafikkkurver blir vårt neste forsøk.
Først, la oss tegne en kvadratisk kurve. Jeg er sikker på at alle leserne har gradert kvadratisk kurve i videregående matematikk klasse, men bare for å oppdatere minnet, presenterer jeg grafer nedenfor. De er plassert side ved side for å lette sammenligningen.
Den åpenbare forskjellen er den omvendte y-aksen på Flash koordinatplass. De ser enkle over alt, ikke sant? Ok, nå er vi klare til å plotte på Flash-koordinatplass.
For å plassere kvadratiske kurver på rett sted, må vi forstå deres tilsvarende ligninger. Kurven tegnet er virkelig avhengig av ligningens koeffisienter (i tilfelle av kvadratiske, de er A, B og C).
Jeg har tatt med en Flash-presentasjon under, slik at du enkelt kan justere disse koeffisientene og få umiddelbar tilbakemelding.
For å studere effektene av individuelle koeffisienter på den generelle kurven foreslår jeg å følge trinnene under for å eksperimentere med Flash-presentasjonen ovenfor.
En annen interessant observasjon er at i løpet av det andre og tredje trinn av det ovennevnte forblir infleksjonspunktet (dvs. vendepunktet) i samme punkt på y-aksen.
Du ser raskt at posisjonering en kurve er noe vanskelig. Den benyttede ligningen er upraktisk hvis vi vil si, finne koordinatene til det laveste punktet på en kurve.
Løsning? Vi omskriver ligningen til ønsket form. Sjekk ut følgende ligning:
\ [f (x) = P (x + Q) ^ 2 + R \]
Det er fortsatt en kvadratisk ligning, men det er tatt en annen form. Nå kan vi enkelt kontrollere minimum og maksimum poeng på kurven. I den forrige Flash-presentasjonen klikker du på knappen "Tilnærming 1" øverst til høyre og spiller med de nye verdiene.
Her er en kort forklaring på koeffisienternes roller:
koeffisient | rolle |
P | Kontroller kurvens bratthet. |
Q | Styr forskyvning av kurvets vendepunkt langs x-akse. |
R | Kontroller forskyvning av kurvens vendepunkt langs y-aksen. |
Likevel er det fortsatt en vanskelig oppgave å få kurven til å passere gjennom et gitt sett med poeng. Vi må nøye forhåndsberegne på papir før vi oversetter det til kode.
Heldigvis er det en bedre løsning. Men før vi går gjennom det, la oss se på ActionScript-implementeringen fra nå av.
Her er ligningene skrevet som ActionScript-funksjoner (sjekk Graphing.as
i kilden nedlasting).
Privat funksjon kvadratisk1 (x: tall, A: tall, b: tall, c: tall): tall // y = a (x ^ 2) + b (x) + c retur A * x * x + b * x + C privatfunksjon kvadratisk2 (x: tall, p: tall, Q: tall, R: tall): tallet // y = p * (x + Q) ^ 2 + retur P * (x + Q) * x + Q) + R
Og her er en implementering av tegnemetoden med Graphics.drawPath ()
. Bare et notat om at alle kurver i denne artikkelen er tegnet på lignende måte.
Først variablene ...
private var poeng: Vector.= Ny Vector. ; private var drawCommand: Vector. = Ny Vector. ;
Nå y-stillingene, beregnet ut fra x-stillingene og de givne koeffisientene.
privat funksjon 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; hvis (isApproach1) poeng [i * 2 + 1] = kvadratisk1 (x, A, B, C) + scene.stageHeight >> 1 annet poeng [i * 2 + 1] = kvadratisk2 (x, a, b , C) + scene.stageHeight >> 1 hvis (i == 0) drawCommand [i] = 1; ellers drawCommand [i] = 2; graphics.clear (); graphics.lineStyle (1); graphics.drawPath (drawCommand, poeng);
(Forvirret om >>
operatør? Ta en titt på denne opplæringen.)
Anta at vi får tre poeng at den kvadratiske kurven må krysse gjennom; hvordan danner vi den tilsvarende ligningen? Nærmere bestemt, hvordan kan vi bestemme koeffisientverdiene til ligningen? Linjær algebra kommer til redning. La oss analysere dette problemet.
Vi vet at kvadratiske ligninger alltid tar form som skrevet i eq. 1 i trinn 1.
\ [f (x) = Aks ^ 2 + Bx + C \ ... (eq \ 1) \]
Siden alle tre koordinatene gitt ligger på samme kurve, må de hver tilfredsstille denne ligningen, med de samme koeffisientene som kurvens likning som vi leter etter. La oss skrive dette ned i ligningsform.
Gitt tre coodinates:
Erstatt disse verdiene i (eq 1). Merk at A, B, C er ukjente for øyeblikket.
\ [f (x) = Aks ^ 2 + Bx + C \ ... (eq \ 1) \]
Skriv om igjen i matriseskjema. Legg merke til at A, B, C er de ukjente vi løser for.
[Latex]
\ begin bmatrix S_y \\ T_y \\ U_y \ end bmatrix =
\ Begynne bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) og 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix
\ begin bmatrix A \\ B \\ C \ end bmatrix \\
[/ Latex]
[Latex]
\ Begynne bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) og 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ venstre (U_x \ høyre) ^ 2 & \ venstre (U_x \ høyre) og 1 \ end bmatrix ^ - 1
\ begin bmatrix S_y \\ T_y \\ U_y \ end bmatrix =
\ Begynne bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) og 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ venstre (U_x \ høyre) ^ 2 & \ venstre (U_x \ høyre) og 1 \ end bmatrix ^ - 1
\ Begynne bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) og 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ left (U_x \ right) ^ 2 & \ left (U_x \ right) & 1 \ end bmatrix
\ begin bmatrix A \\ B \\ C \ end bmatrix \\
[/ Latex]
[Latex]
\ Begynne bmatrix
\ left (S_x \ right) ^ 2 & \ left (S_x \ right) og 1 \\
\ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ venstre (U_x \ høyre) ^ 2 & \ venstre (U_x \ høyre) og 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]
Selvfølgelig kan vi bruke samtidige ligninger i stedet, men jeg foretrekker å bruke matriser fordi det er enklere. (Redaktørens notat: så lenge du forstår matriser, det er det!)
Vi får inversen til K og formere med J-matrisen for å få L. Etter at vi har løslatt for A, B, C, erstatter vi bare den kvadratiske ligningen. Dermed har vi en kvadratisk kurve som går gjennom alle tre punktene.
Som nevnt i det forrige trinnet, må vi utføre en 3x3 matrix inversjon og multiplikasjon. Action s flash.geom.matrix
klassen vil ikke kunne hjelpe i dette. Selvfølgelig har vi et valg å bruke flash.geom.Matrix3D
, klassen, men jeg foretrekker Coral-biblioteket fordi jeg kan pry inn i disse tilpassede klassene og undersøke hva som skjer under hetten. Jeg personlig finner dette veldig nyttig når du er i tvil om riktig bruk av kommandoer selv etter å ha lest API-dokumentasjonen.
Så last ned og legg de utpakede Coral-filene inn i prosjektkildemappen.
Her er et utvalg av resultatet. Prøv å reposisjonere de røde prikkene og se den kvadratiske kurven som blir revet for å krysse gjennom alle tre punktene.
Du kan finne hele skriptet i Draw_curve.as.
Følgende ActionScript er bare for å aktivere musekontroller på de små punktene.
offentlig funksjon Draw_Curve () // sette opp kontroller c1 = ny sirkel (0xFF0000); addChild (c1); c1.x = stage.stageWidth * 0.2; c1.y = scene.stageHeight >> 1; c2 = ny sirkel (0xFF0000); addChild (c2); c2.x = scene.stageWidth * 0,5; c2.y = scene.stageHeight >> 1; c3 = ny sirkel (0xFF0000); addChild (c3); c3.x = scene.stageWidth * 0,8; c3.y = scene.stageHeight >> 1; c1.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c1.addEventListener (MouseEvent.MOUSE_UP, flytt); c2.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c2.addEventListener (MouseEvent.MOUSE_UP, flytt); c3.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c3.addEventListener (MouseEvent.MOUSE_UP, flytt); redraw () privat funksjon flytte (e: MouseEvent): void if (e.type == "mouseDown") e.target.startDrag () e.target.addEventListener (MouseEvent.MOUSE_MOVE, oppdatering); annet hvis (e.type == "mouseUp") e.target.stopDrag (); e.target.removeEventListener (MouseEvent.MOUSE_MOVE, oppdatering); personlig funksjon oppdatering (e: MouseEvent): void redraw ();
Kjernen ligger i tegne
funksjon. Jeg har uthevet matrisoperasjonen og den kvadratiske funksjonen for redraw-prosessen.
privat funksjon 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 poeng: Vector.= Ny Vector. ; var cmd: vektor. = Ny Vector. ; for (var jeg: 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 at matrisen K ble initialisert og invertert før den ble lagt på matrisen J.
De føye ()
funksjon multipliserer nåværende matrise, J, med inngangsmatrisen, K, plassert til venstre. En annen bemerkelsesverdig detalj er at vi ikke bruker alle rader og kolonner i K og J matriser. Men siden matrix inversjon bare kan skje med en firkantet matrise, må vi fylle inn 4. rad, 4. kolonneelement av K med 1. (Det er ikke nødvendig å gjøre dette for J fordi vi ikke trenger inversjon i beregningen vår. ) Således kan du se alle de andre elementene er 0, bortsett fra den første kolonnen.
Så det er alt for å tegne kvadratiske kurver. La oss gå videre til kubiske kurver.
Igjen, vi får en liten revisjon av grafering av disse kurvene. Sjekk ut følgende bilde:
Når du sammenligner denne kurven med den av kvadratiske, vil du legge merke til at den er brattere, og at en del av kurven er under x-aksen. En halv er speilet vertikalt, sammenlignet med en kvadratisk.
Jeg har tatt med følgende Flash-presentasjon for å la deg eksperimentere med koeffisientene til en kubisk ligning. Prøv å justere verdien av A fra positiv til negativ og observere forskjellen i produsert kurve.
Her er den viktige delen av implementeringen av grafen ovenfor:
privat funksjon 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; poeng [i * 2 + 1] = cubic1 (x, a, b, c, d) + scene.stageheight >> 1 hvis (i == 0) drawCommand [i] = 1; ellers drawCommand [i] = 2; graphics.clear (); graphics.lineStyle (1); graphics.drawPath (drawCommand, poeng); privat funksjon cubic1 (x: tall, A: tall, b: tall, c: tall, d: tall): tall // y = a (x ^ 3) + b (x ^ 2) + c + D retur A * x * x * x + b * x * x + c * x + d
Igjen er det vanskelig å plassere den kubiske kurven i henhold til et sett med punkter det krysser gjennom. Igjen refererer vi til lineær algebra for et alternativ.
Vi vet fra trinn 6 at koeffisientene til en kvadratisk ligning kan beregnes basert på tre gitte punkter, og kurven trukket fra den vil krysse gjennom disse punktene. En lignende tilnærming kan utføres med noen fire gitt poeng for å oppnå en kubisk ligning:
Erstatt disse koordinatene i (eq 2). Merk at A, B, C, D er ukjente.
\ [g (x) = Aks ^ 3 + Bx ^ 2 + Cx + D \ ... (eq \ 2) \]
Men nå skal vi håndtere en 4x4 matrise i stedet for 3x3 matrise:
\ (
\ begin bmatrix S_y \\ T_y \\ U_y \\ V_y \ end bmatrix =
\ Begynne bmatrix
\ venstre (S_x \ høyre) ^ 3 & \ venstre (S_x \ høyre) ^ 2 & \ venstre (S_x \ høyre) og 1 \\
\ left (T_x \ right) ^ 3 & \ left (T_x \ right) ^ 2 & \ left (T_x \ right) og 1 \\
\ venstre (U_x \ høyre) ^ 3 & \ venstre (U_x \ høyre) ^ 2 & \ venstre (U_x \ høyre) og 1 \\
\ venstre (V_x \ høyre) ^ 3 & \ venstre (V_x \ høyre) ^ 2 & \ venstre (V_x \ høyre) & 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
\)
Nå skal vi bruke alle elementene i 4x4-matrisen for Q og hele første kolonne for P. Da blir Q omvendt og påført P.
Igjen, setter vi opp musekontrollene for å tillate å dra disse punktene. Når noen av disse punktene blir trukket, skjer det regelmessig omberegning og gjenoppretting av kurven.
offentlig funksjon Draw_Curve2 () // setting up controls c1 = ny sirkel (0xFF0000); addChild (c1); c1.x = stage.stageWidth * 0.2; c1.y = scene.stageHeight >> 1; c2 = ny sirkel (0xFF0000); addChild (c2); c2.x = scene.stageWidth * 0,4; c2.y = scene.stageHeight >> 1; c3 = ny sirkel (0xFF0000); addChild (c3); c3.x = scene.stageWidth * 0,6; c3.y = scene.stageHeight >> 1; c4 = ny sirkel (0xFF0000); addChild (c4); c4.x = scene.stageWidth * 0,8; c4.y = scene.stageHeight >> 1; c1.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c1.addEventListener (MouseEvent.MOUSE_UP, flytt); c2.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c2.addEventListener (MouseEvent.MOUSE_UP, flytt); c3.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c3.addEventListener (MouseEvent.MOUSE_UP, flytt); c4.addEventListener (MouseEvent.MOUSE_DOWN, flytte); c4.addEventListener (MouseEvent.MOUSE_UP, flytt); tegne (); privat funksjon flytte (e: MouseEvent): void if (e.type == "mouseDown") e.target.startDrag () e.target.addEventListener (MouseEvent.MOUSE_MOVE, update); annet hvis (e.type == "mouseUp") e.target.stopDrag (); e.target.removeEventListener (MouseEvent.MOUSE_MOVE, oppdatering); personlig funksjon oppdatering (e: MouseEvent): void redraw ();
tegne
er den avgjørende funksjonen der alt skjedde.
privat funksjon redraw (): void var igjen: 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 riktig: 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 (til venstre); // f (x) = A (x ^ 3) + B (x ^ 2) + C (x) + D graphics.clear (); var poeng: Vector.= Ny Vector. ; var cmd: vektor. = Ny Vector. ; for (var jeg: 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);
Til slutt, la oss se på produktet. Klikk og flytt de røde prikkene for å se kubisk kurve tegnet for å passere gjennom alle disse punktene.
Vi har nettopp gått gjennom tegningspolynomene av grad 2 og 3 (kvadratisk og kubisk). Fra vår erfaring kan vi forutse at beregning for polynom av grad 4 (quintic) vil kreve fem poeng, som vil kreve 5x5 matrise, og så videre for polynomier med enda høyere grader.
dessverre, Korall
og flash.geom.Matrix3D
bare gi 4x4 matriser, så du vil skrive din egen klasse hvis behovet kommer. Det er sjelden kreves i spill, skjønt.
La oss prøve å bruke vår kunnskap til å dele regioner på vår scene. Dette krever en viss revisjon av likningens ulikheter. Sjekk ut bildet nedenfor.
Dette bildet over viser en kurve som deler regionene i to:
Det er ikke vanskelig å forstå dette konseptet. Faktisk har du allerede eksperimentert med dette i trinn 11 som du tweaked koeffisientene til kubisk formel. Tenk i koordinatsystemet at det er et uendelig antall kurver, alle differensiert av bare en liten endring i D:
Så her er utvalget av produksjon for kvadratisk kurve. Du kan prøve å flytte den røde prikken rundt og se regionene farget.
Her er den viktige ActionScript-koden. Sjekk ut hele skriptet i Region_Curve.as
privat funksjon redraw (): void var igjen: 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 riktig: Matrix3d = ny Matrix3d (c1.y, 0, 0, 0, c2.y, 0, 0, 0, c3.y, 0, 0, 0, 0, 0, 0, 0 ); right.append (til venstre); // Var = A (x ^ 2) + B (x) + C for hver (var-element: Sirkel i bakgrunnen) var D: Nummer = right.n11 * item.x * item.x + right.n21 * element .x + right.n31; //trace(background[i].y); hvis (item.y> D) item.color = 0; ellers item.color = 0xAAAAAA;
Her er prøven med hensyn til kubisk kurve.
Og implementeringen som følger med den. Igjen, fullt skript er inne Region_Curve2.as
// Var = A + B (x) + C (x ^ 2) for hver (var-element: Sirkel i bakgrunnen) 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); hvis (item.y> D) item.color = 0; ellers item.color = 0xAAAAAA;
Hva med noen tweaks for å endre fargen over forskjellige kurver? Igjen, museklikk på de røde prikkene og se gradientendringer over skjermen.
Her er den viktige ActionScript-utdragen hentet fra Region_Curve3.as
. Først av alt vil vi finne ut maksimums- og minimumsforskjellen fra den opprinnelige kurven.
var max: tall = 0; var min: tall = 0; var Ds: Vector.= Ny Vector. ; // Var = A (x ^ 2) + B (x) + C for hver (var-element: Sirkel i bakgrunnen) var D: Nummer = right.n11 * item.x * item.x + right.n21 * element .x + right.n31; var offset: Number = item.y - D; Ds.push (offset); hvis (item.y> D && offset> max) max = offset; ellers hvis (item.y < D && offset < min) min = offset;
Når du er ferdig, bruker vi den til å fargelegge de enkelte prikkene.
// fargevariasjoner basert på offset var farge: Nummer for (var i: int = 0; i < background.length; i++) if (Ds[i] > 0) color = Ds [i] / max * 255 // regne farge til spor i bakgrunnen [i] .color = farge<<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å alt for tegning av kurver. Neste opp, finne røtter av en kvadratisk og kubisk kurve. Takk for at du leste. Del hvis du ser noen virkelige applikasjoner som utnytter denne opplæringen.