Vi har taklet tegningskurver og funnet sine kvadratiske og kubiske røtter, samt praktiske applikasjoner for bruk av kvadratiske røtter i spill. Nå, som lovet, ser vi på søknader for å finne kubikk røtter, samt kurver 'gradienter og normaler, som å gjøre objekter sprette av buede overflater. La oss gå!
La oss ta en titt en praktisk bruk av denne matte:
I denne demonstrasjonen hopper "skipet" av kantene på SWF og kurven. Den gule prikken representerer nærmeste punkt på skipet som ligger på kurven. Du kan justere kurvenes form ved å dra de røde prikkene og justere bevegelsen på skipet ved hjelp av piltastene.
La oss se på scenariet der et punkt ligger i nærheten av en kvadratisk kurve. Hvordan beregner du den korteste avstanden mellom punktet og kurven?
Vel, la oss starte med Pythagoras teoremåte.
\ [
La \ the \ point \ be \ (x_p, \ y_p) \\
og \ call \ the \ nearest \ point \ on \ the \ curve \ (x_c, \ y_c) \\
Deretter:\\
z ^ 2 = x ^ 2 + y ^ 2 \\
z ^ 2 = (x_c-x_p) ^ 2 + (y_c-y_p) ^ 2 \\
Gitt \ y_c = ax_c ^ 2 + bx_c + c, \\
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
Du kan se at vi har erstattet \ (y_c \) med den kvadratiske ligningen. Med et blikk kan vi se den høyeste kraften er 4. Således har vi en quartic ligningen. Alt vi trenger å gjøre er å finne et minimumspunkt i denne ligningen for å gi oss den korteste avstanden fra et punkt til en kvadratisk kurve.
Men før det må vi forstå gradienter på en kurve ...
Før vi ser på problemet med å minimere en kvartsligning, la oss prøve å forstå gradienter av en kurve. En rett linje har bare en gradient. Men en kvadratisk kurves gradient avhenger av hvilket punkt på kurven vi refererer til. Ta en titt på Flash-presentasjonen nedenfor:
Dra de røde prikkene rundt for å endre den kvadratiske kurven. Du kan også spille med skyvehåndtaket for å endre plasseringen av den blå prikken langs x. Etter hvert som den blå prikken endres, vil også gradienten trekkes.
Dette er hvor kalkulasjonen vil komme til nytte. Du har kanskje gjettet at differensiering av en kvadratisk ligning vil gi deg kurvens gradient.
\ [
f (x) = økse ^ 2 + bx + c \\
\ frac df (x) dx = 2ax + b
\]
Så \ (\ frac df (x) dx \) er gradienten til en kvadratisk kurve, og den er avhengig av \ (x \) koordinaten. Vel, gode ting, vi har en metode for å håndtere dette: DIFF1 (x: Number)
vil returnere verdien etter en enkelt differensiering.
For å tegne gradienten trenger vi en ligning som representerer linjen, \ (y = mx + c \). Koordinatet til det blå punktet \ ((x_p, y_p) \) vil bli erstattet av \ (x \) og \ (y \), og gradienten av linjen funnet gjennom differensiering vil gå inn i \ (m \). Dermed kan y-avsnitten av linje, \ (c \) beregnes gjennom noe algebraarbeid.
Sjekk ut AS3:
var x: tall = s.value var y: tall = quadratic_equation.fx_of (s.value) 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);
Vær alltid oppmerksom på den omvendte y-aksen av Flash-koordinatplass som vist på bildet nedenfor. Ved første øyekast kan diagrammet til høyre virke som en negativ gradient - men på grunn av den omvendte y-aksen er det faktisk en positiv gradient.
Det samme gjelder for minimumspunktet som angitt nedenfor. På grunn av den omvendte y-aksen ser minimumspunktet i kartesisk koordinatrom (på (0,0)) som et maksimum i Flash koordinatrom. Men ved å referere til opprinnelsesstedet i Flash-koordinatrom i forhold til den kvadratiske kurven, er det faktisk et minimumspunkt.
La oss si at jeg er interessert i å finne det laveste punktet på en kurve - hvordan går jeg videre? Sjekk ut bildet nedenfor (begge tallene er i samme koordinatrom).
For å få minimumspunktet, vil vi bare likestille \ (\ frac df (x) dx = 0 \), etter hvert ser vi etter punktet hvor gradienten er null. Men som vist ovenfor viser det seg at maksimumpunktet på en kurve også tilfredsstiller denne tilstanden. Så hvordan diskriminerer vi mellom disse to sakene?
La oss prøve differensiering av andre grad. Det gir oss graden av endring av gradienten.
\ [
\ frac df (x) dx = 2ax + b \\
\ frac df ^ 2 (x) dx ^ 2 = 2a
\]
Jeg vil forklare med henvisning til bildet nedenfor (tegnet i kartesisk koordinatrom). Vi kan se at når vi øker langs x-aksen, endres gradienten fra negativ til positiv. Så byttehastigheten skal være a positiv verdi.
Vi kan også se at når \ (\ frac df ^ 2 (x) dx ^ 2 \) er positiv, er det et minimumspunkt på kurven. Omvendt hvis frekvensen er negativ, er et maksimumpunkt til stede.
Nå er vi klare til å løse problemet som presenteres i trinn 1. Husk kvartsligningen (hvor høyeste grad er 4) vi ankom:
\ [
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
Den samme kvartiske ligningen, plottet
Husk at vi er interessert i å finne minimumspunktet på denne kurven, fordi det tilsvarende punktet på den opprinnelige kvadratiske kurven vil være punktet som er i minste avstand fra den røde prikken.
Så, la oss differensiere kvartsfunksjonen for å komme til gradienten av denne kurven, og likevel korrigere graden av denne kvartsfunksjonen til null. Du vil se at gradienten faktisk er en kubisk funksjon. Jeg vil vise interesserte lesere til Wolframs side; for denne opplæringen vil jeg bare plukke resultatet av algebra-arbeidene deres:
\ [
\ Frac d (z ^ 2) dx =
2 (x_c-x_p) + 2 (ax_c ^ 2 + bx_c + c - y_p) (2ax_c + b) \\
\ xc) ^ 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 \\
Sammenlign \ med \ cubic \ ligning \\
Ax ^ 3 + Bx ^ 2 + Cx + D = 0 \\
A = 2a ^ 2 \\
B = 3AB \\
C = b ^ + 2 2ac-2ay_p + 1 \\
D = bc-by_p-x_p
\]
Løs for røttene til denne (ganske rotete) kubiske funksjonen, og vi kommer til koordinatene til de tre blå punktene som angitt ovenfor.
Neste, hvordan filtrerer vi resultatene våre for minimumspunktet? Husk fra forrige trinn at et minimumspunkt har en endringshastighet som er positiv. For å få denne endringsgraden, skille mellom kubisk funksjon som representerer gradient. Hvis endringsraten for det oppgitte blåpunktet er positivt, er det en av minimumspunkene. Å få de minimumspunkt, det vi er interessert i, velg punktet med høyeste endringshastighet.
Så her er en prøveimplementering av ideen som er forklart ovenfor. Du kan trekke de røde prikkene rundt for å tilpasse den kvadratiske kurven. Den blå prikken kan også bli trukket. Når du flytter den blå prikken, vil den gule bli flyttet slik at avstanden mellom de blå og gule punktene vil være minst blant alle punkter på kurven.
Når du samhandler med Flash-presentasjonen, kan det være tider hvor tre gule prikker vises på en gang. To av disse ble falmet ut, refererer til røttene hentet fra beregningen, men avvist fordi de ikke er de nærmeste punktene på kurven til den blå prikken.
Så her er ActionScript-implementeringen av ovenstående. Du kan finne hele skriptet i Demo2.as
.
Først av alt må vi tegne den kvadratiske kurven. Legg merke til at matrisen m2
vil bli henvist til for ytterligere beregning.
privat funksjon redraw_quadratic_curve (): void var cmd: Vector.= Ny Vector. ; var koordinat: Vector. = Ny Vector. ; // redraw kurve; m1 = ny Matrix3d (kurvepoeng [0] .x * kurvepoeng [0] .x, kurvepoeng [0] .x, 1, 0, kurvepoeng [1] .x * kurvepoeng [1] .x, kurvepoeng [1] .x , 1, 0, kurvepoeng [2] .x * kurvepoeng [2] .x, kurvepoeng [2] .x, 1, 0, 0,0,0,1); m2 = ny Matrix3d (kurvepoeng [0] .y, 0, 0, 0, kurvepoeng [1] .y, 0, 0, 0, kurvepoeng [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); for (var jeg: 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);
Og her er den som implementerer det matematiske konseptet som er forklart. c1
refererer til et punkt som er tilfeldig plassert på scenen.
privat funksjon recalculate_distance (): void var a: Number = m2.n11; var b: tall = m2.n21; var c: tall = m2.n31; / * f (x) = Aks ^ 3 + Bx ^ 2 + Cx + D * / var A: Nummer = 2 * a * a var B: Nummer = 3 * b * a var C: Nummer = b * b + 2 * c * a - 2 * a * c1.y +1 var D: Nummer = c * b - b * c1.y - c1.x quartic_gradient = nytt EqCubic (); quartic_gradient.define (A, B, C, D); quartic_gradient.calcRoots (); røtter = quartic_gradient.roots_R; var valgt: Nummer = røtter [0]; hvis (! erNaN (røtter [1]) &&! !NaN (røtter [2])) // beregne gradient og gradientgradient for alle ekte røtter var quartic_rate: Vector.= Ny Vector. ; for (var jeg: 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] > quartic_rate [j - 1]) valgt = røtter [j]; // plassere de ekstra røttene i demo position_extras (); ellers // fjern de ekstra røttene i demo kill_extras (); intersec_points [0] .x = valgt intersec_points [0] .y = quadratic_equation.fx_of (valgt);
La oss gjøre bruk av dette konseptet for å oppdage overlappingen mellom en sirkel og en kurve.
Ideen er enkel: Hvis avstanden mellom den blå prikken og den gule prikken er mindre enn den blå prikkens radius, har vi en kollisjon. Sjekk ut demoen nedenfor. De interaktive elementene er de røde prikkene (for å styre kurven) og den blå prikken. Hvis den blå prikken kolliderer med kurven, vil den fade litt ut.
Vel, koden er ganske enkel. Sjekk ut hele kilden i CollisionDetection.as
.
graphics.moveTo (intersec_points [0] .x, intersec_points [0] .y); graphics.lineTo (c1.x, c1.y); var avstand: Nummer = Math2.Pythagoras (intersec_points [0] .x, intersec_points [0] .y, c1.x, c1.y) hvis < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);
Så nå som vi vet når kollisjon vil oppstå, la oss prøve å programmere noe kollisionsrespons. Hva med å hoppe av overflaten? Ta en titt på Flash-presentasjonen nedenfor.
Du kan se skipet (trekanten form), er omgitt av en sirkel (gjennomsiktig blå). Når sirkelen kolliderer med kurven, vil skipet hoppe av overflaten.
Her er ActionScript for å kontrollere skipet.
offentlig funksjon CollisionDetection2 () / ** * Instantiation av skipet og dets blå-ish sirkulære område * / skip = nytt trekant (); addChild (skip); ship.x = Math.random () * scene.stageWidth; ship.y = scene.stageHeight * 0,8; c1 = ny sirkel (0x0000ff, 15); addChild (c1); c1.alpha = 0.2; / ** * Skipets hastighet * / velo = ny Vector2D (0, -1); updateShip (); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener (KeyboardEvent.KEY_UP, handleKey); stage.addEventListener (Event.EXIT_FRAME, handleEnterFrame); / ** * Kurven og beregningene * / quadratic_equation = new EqQuadratic (); curve_points = ny vektor.; fylle (kurvepoeng, 0xff0000, 3); intersec_points = ny vektor. ; fylle (intersec_points, 0xffff00, 3, false); redraw_quadratic_curve (); Private Function handleKey (e: KeyboardEvent): void if (e.type == "keyDown") hvis (e.keyCode == Keyboard.UP) isUp = true; ellers hvis (e.keyCode == Keyboard.DOWN) isDown = true; hvis (e.keyCode == Keyboard.LEFT) isLeft = true; ellers hvis (e.keyCode == Tastatur.RETT) isRight = true; hvis (e.type == "keyUp") hvis (e.keyCode == Keyboard.UP) isUp = false; ellers hvis (e.keyCode == Keyboard.DOWN) isDown = false; hvis (e.keyCode == Keyboard.LEFT) isLeft = false; ellers hvis (e.keyCode == Tastatur.RETT) isRight = false; Private Function handleEnterFrame (e: Event): void / ** * Kontroller størrelsen * / hvis (isUp) velo.setMagnitude (Math.min (velo.getMagnitude () + 0.2, 3)); ellers hvis (isDown) velo.setMagnitude (Math.max (velo.getMagnitude () - 0.2, 1)); / ** * Kontroller retningen * / hvis (isRight) velo.setAngle (velo.getAngle () + 0.03); ellers hvis (isLeft) velo.setAngle (velo.getAngle () - 0.03); recalculate_distance (); hvis (avstand < 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;
Du kan se at tastaturkontrollene oppdaterer flagg for å angi om du trykker på venstre, opp, høyre eller ned-tasten. Disse flaggene vil bli fanget av enterframe-hendelseshåndtereren og oppdatere skipets størrelse og retning.
Jeg har allerede dekket vektorkalkylen for reflektorvektoren i dette innlegget. Her skal jeg bare dekke hvordan man får den normale vektoren fra gradient.
\ [
\ Frac df (x) dx = gradient \\
linje \ gradient = \ frac y x \\
Anta \ gradient = 0.5 \\
y = 0,5 \\
x = 1 \\
Vector \ of \ left \ normal =
\ begin bmatrix -1 \\ 0.5 \ end bmatrix \\
Vector \ of \ right \ normal =
\ begin bmatrix 1 \\ - 0.5 \ end bmatrix
\]
Så vil ActionScript nedenfor implementere det matematiske konseptet som er forklart i forrige trinn. Sjekk ut de uthevede linjene:
privatfunksjonstopp (): void var gradient: Number = quadratic_equation.diff1 (intersec_points [0] .x); var grad_vec: Vector2D = ny Vector2D (1, gradient); var left_norm: Vector2D = grad_vec.getNormal (false); var right_norm: Vector2D = grad_vec.getNormal (); var valgt_vec: Vector2D; hvis (velo.dotProdukt (left_norm)> 0) selected_vec = left_norm annet selected_vec = right_norm var selected_unit: Vector2D = selected_vec.normalise (); var proj: Nummer = velo.dotProdukt (valgt enhet); chosen_unit.scale (-2 * proj); velo = velo.add (selected_unit);
Vel, takk for din tid! Hvis du har funnet dette nyttig, eller har noen spørsmål, legg igjen noen kommentarer.