We hebben tekencurven aangepakt en hun kwadratische en kubieke wortels gevonden, evenals handige toepassingen voor het gebruik van kwadratische wortels in games. Nu, zoals beloofd, zullen we kijken naar aanvragen om te vinden kubiek wortels, evenals hellingen en normaalten van curven, zoals het laten terugspringen van gebogen oppervlakken. Laten we gaan!
Laten we eens kijken naar een praktisch gebruik van deze wiskunde:
In deze demo botst het "schip" van de randen van de SWF en de curve. De gele stip vertegenwoordigt het dichtstbijzijnde punt van het schip dat op de bocht ligt. U kunt de vorm van de curve aanpassen door de rode stippen te slepen en de beweging van het schip aan te passen met behulp van de pijltoetsen.
Laten we het scenario bekijken waarbij een punt zich in de buurt van een kwadratische curve bevindt. Hoe bereken je de kortste afstand tussen het punt en de curve?
Laten we beginnen met de stelling van Pythagoras.
\ [
Laat \ het \ punt \ zijn \ (x_p, \ y_p) \\
en \ call \ the \ closest \ point \ on \ the \ curve \ (x_c, \ y_c) \\
Dan:\\
z ^ 2 = x ^ 2 + y ^ 2 \\
z ^ 2 = (x_c-x_p) ^ 2 + (y_c-y_p) ^ 2 \\
Given \ y_c = ax_c ^ 2 + bx_c + c, \\
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
Je kunt zien dat we \ (y_c \) hebben vervangen door de kwadratische vergelijking. In één oogopslag zien we dat de hoogste macht 4 is. We hebben dus een quartic vergelijking. Alles wat we moeten doen is een minimum punt in deze vergelijking vinden om ons de kortste afstand te geven van een punt naar een kwadratische curve.
Maar daarvoor moeten we gradiënten op een curve begrijpen ...
Voordat we kijken naar het probleem van het minimaliseren van een quartische vergelijking, laten we proberen gradiënten van een curve te begrijpen. Een rechte lijn heeft slechts één verloop. Maar de gradiënt van een kwadratische curve is afhankelijk van het punt op de curve waarnaar we verwijzen. Bekijk de Flash-presentatie hieronder:
Versleep de rode stippen rond om de kwadratische curve te wijzigen. Je kunt ook met de hendel van de slider spelen om de positie van de blauwe stip langs x te veranderen. Naarmate de blauwe stip verandert, wordt ook het verloop getekend.
Dit is waar calculus van pas zal komen. Je hebt misschien geraden dat het differentiëren van een kwadratische vergelijking je het verloop van de curve zou geven.
\ [
f (x) = ax ^ 2 + bx + c \\
\ frac df (x) dx = 2ax + b
\]
Dus \ (\ frac df (x) dx \) is de helling van een kwadratische kromme en deze is afhankelijk van de coördinaat \ (x \). Nou, goed dat we een methode hebben om dit aan te pakken: diff1 (x: Number)
retourneert de waarde na een enkele differentiatie.
Om het verloop te tekenen, hebben we een vergelijking nodig om de lijn weer te geven, \ (y = mx + c \). De coördinaat van het blauwe punt \ ((x_p, y_p) \) wordt vervangen door de \ (x \) en \ (y \) en het verloop van de lijn gevonden door differentiatie gaat naar \ (m \). Het y-snijpunt van lijn, \ (c \) kan dus worden berekend door middel van een of ander algebrawerk.
Bekijk de AS3:
var x: Number = s.value var y: Number = 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);
Houd altijd rekening met de omgekeerde y-as van de Flash-coördinaatruimte zoals weergegeven in de onderstaande afbeelding. Op het eerste gezicht lijkt het diagram rechts misschien een negatief verloop, maar vanwege de omgekeerde y-as is het eigenlijk een positief verloop.
Hetzelfde geldt voor het minimum punt zoals hieronder aangegeven. Vanwege de omgekeerde y-as, lijkt het minimumpunt in cartesiaanse coördinaatruimte (bij (0,0)) als een maximum in Flash-coördinaatruimte. Maar door te verwijzen naar de locatie van oorsprong in de Flash-coördinaatruimte ten opzichte van de kwadratische curve, is het eigenlijk een minimumpunt.
Laten we nu zeggen dat ik geïnteresseerd ben in het vinden van het laagste punt op een curve - hoe ga ik verder? Bekijk de afbeelding hieronder (beide figuren bevinden zich in dezelfde coördinaatruimte).
Om het minimumpunt te krijgen, stellen we gewoon \ (\ frac df (x) dx = 0 \) gelijk, omdat we per definitie op zoek zijn naar het punt waar het verloop nul is. Maar zoals hierboven getoond, blijkt dat het maximale punt op een curve ook aan deze voorwaarde voldoet. Dus hoe kunnen we onderscheid maken tussen deze twee gevallen?
Laten we differentiatie van de tweede graad proberen. Het geeft ons de mate van verandering van het verloop.
\ [
\ frac df (x) dx = 2ax + b \\
\ frac df ^ 2 (x) dx ^ 2 = 2a
\]
Ik leg het uit met verwijzing naar de afbeelding hieronder (getekend in cartesiaanse coördinatenruimte). We kunnen zien dat, naarmate we langs de x-as stijgen, de gradiënt verandert van negatief in positief. Dus de snelheid van verandering moet een zijn positief waarde.
We kunnen ook zien dat wanneer \ (\ frac df ^ 2 (x) dx ^ 2 \) positief is, er een minimumpunt op de curve is. Omgekeerd, als de snelheid negatief is, is een maximumpunt aanwezig.
Nu zijn we klaar om het probleem op te lossen dat in Stap 1 wordt gepresenteerd. Denk aan de quartische vergelijking (waar de hoogste graad 4 is) waar we aankwamen:
\ [
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
Dezelfde quartische vergelijking, uitgezet
Vergeet niet dat we geïnteresseerd zijn om het minimumpunt op deze curve te vinden, omdat het corresponderende punt op de oorspronkelijke kwadratische curve het punt is dat zich op de minimumafstand van de rode stip bevindt.
Laten we dus de quartische functie differentiëren om de gradiënt van deze curve te krijgen, en dan de gradiënt van deze quartische functie gelijkstellen aan nul. Je zult zien dat het verloop eigenlijk een kubieke functie is. Ik zal geïnteresseerde lezers verwijzen naar de pagina van Wolfram; voor deze tutorial pluk ik het resultaat van hun algebra-werk:
\ [
\ 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) \\
Vergelijk \ gradient \ to \ 0 \\
\ Frac d (z ^ 2) dx = 0 \\
2a ^ 2 (x_c) ^ 3 + 3 bis bis (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p-x_p) = 0 \\
Vergelijk \ met \ kubieke \ vergelijking \\
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
\]
Los op voor de wortels van deze (nogal rommelige) kubieke functie en we komen bij de coördinaten van de drie blauwe punten zoals hierboven aangegeven.
Hoe filteren we onze resultaten vervolgens op het minimum? Bedenk uit de vorige stap dat een minimumpunt een veranderingssnelheid heeft die positief is. Om deze mate van verandering te verkrijgen, moet u de kubieke functie die de gradiënt vertegenwoordigt onderscheiden. Als de snelheid van verandering voor het gegeven blauwe punt positief is, is het dat wel een van de de minimum punten. Te krijgen de minimum punt, degene waar we in geïnteresseerd zijn, kies het punt met de hoogste mate van verandering.
Dus hier is een voorbeeldimplementatie van het hierboven toegelichte idee. Je kunt de rode stippen rond slepen om je kwadratische curve aan te passen. De blauwe stip kan ook worden gesleept. Terwijl u de blauwe stip verplaatst, wordt de gele punt zodanig verplaatst dat de afstand tussen de blauwe en gele punten minimaal is tussen alle punten op de curve..
Terwijl u de Flash-presentatie gebruikt, kunnen er soms drie gele stippen tegelijk verschijnen. Twee hiervan, vervaagd, verwijzen naar de wortels verkregen uit de berekening maar afgewezen omdat ze niet de dichtstbijzijnde punten op de curve naar de blauwe stip zijn.
Dit is de ActionScript-implementatie van het bovenstaande. Je kunt het volledige script vinden in Demo2.as
.
Allereerst moeten we de kwadratische curve tekenen. Merk op dat de matrix m2
zal worden doorverwezen voor verdere berekening.
private function redraw_quadratic_curve (): void var cmd: Vector.= nieuwe Vector. ; var coord: Vector. = nieuwe Vector. ; // opnieuw tekenen curve; m1 = nieuwe Matrix3d (curve-punten [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, curve-punten [2] .x * curve-punten [2] .x, curve-punten [2] .x, 1, 0, 0,0,0,1); m2 = nieuwe Matrix3d (curvepunten [0] .y, 0, 0, 0, curve_punten [1] .y, 0, 0, 0, curve_punten [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 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);
En hier is degene die het wiskundige concept implementeert. c1
verwijst naar een willekeurig punt op het podium.
private function recalculate_distance (): void var a: Number = m2.n11; var b: Number = m2.n21; var c: Number = m2.n31; / * f (x) = Ax ^ 3 + Bx ^ 2 + Cx + D * / var A: Number = 2 * a * a var B: Number = 3 * b * a var C: Number = b * b + 2 * c * a - 2 * a * c1.y +1 var D: Number = 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 gekozen: Number = roots [0]; if (! isNaN (roots [1]) &&! isNaN (roots [2])) // bereken de gradiënt en de gradiënt van alle echte wortels var var quartic_rate: Vector.= nieuwe Vector. ; for (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] > quartic_rate [j - 1]) selected = roots [j]; // plaats de extra roots in demo position_extras (); else // verwijder de extra roots in demo kill_extras (); intersec_points [0] .x = selected intersec_points [0] .y = quadratic_equation.fx_of (selected);
Laten we gebruik maken van dit concept om de overlapping tussen een cirkel en een curve te detecteren.
Het idee is simpel: als de afstand tussen de blauwe stip en de gele stip kleiner is dan de straal van de blauwe stip, hebben we een botsing. Bekijk de demo hieronder. De interactieve items zijn de rode stippen (om de curve te besturen) en de blauwe stip. Als de blauwe stip tegen de curve botst, zal deze een beetje vervagen.
Nou, de code is vrij eenvoudig. Bekijk de volledige bron in 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) if (afstand < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);
Dus nu we weten wanneer een botsing zal plaatsvinden, proberen we een botsingsreactie te programmeren. Hoe zit het met het stuiteren van het oppervlak? Bekijk de onderstaande Flash-presentatie.
Je ziet het schip (driehoeksvorm), is omgeven door een cirkel (doorschijnend blauw). Zodra de cirkel tegen de bocht botst, kaatst het schip van het oppervlak af.
Dit is het ActionScript om het schip te besturen.
public function CollisionDetection2 () / ** * Instantiation of ship & its blue-ish circular area * / ship = new Triangle (); addChild (schip); ship.x = Math.random () * stage.stageWidth; ship.y = stage.stageHeight * 0.8; c1 = nieuwe cirkel (0x0000ff, 15); addChild (c1); c, aa = 0,2; / ** * Ship's velocity * / velo = new Vector2D (0, -1); updateShip (); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener (KeyboardEvent.KEY_UP, handleKey); stage.addEventListener (Event.EXIT_FRAME, handleEnterFrame); / ** * De curve en de berekeningen * / quadratic_equation = new EqQuadratic (); curve_points = nieuwe Vector.; vullen (curve_punten, 0xff0000, 3); intersec_points = nieuwe Vector. ; vullen (intersec_points, 0xffff00, 3, false); redraw_quadratic_curve (); private function handleKey (e: KeyboardEvent): void if (e.type == "keyDown") if (e.keyCode == Keyboard.UP) isUp = 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; private function handleEnterFrame (e: Event): void / ** * Regel de magnitude * / if (isUp) velo.setMagnitude (Math.min (velo.getMagnitude () + 0.2, 3)); else if (isDown) velo.setMagnitude (Math.max (velo.getMagnitude () - 0.2, 1)); / ** * Besturing van de richting * / if (isRight) velo.setAngle (velo.getAngle () + 0.03); else if (isLeft) velo.setAngle (velo.getAngle () - 0.03); recalculate_distance (); als (afstand < 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;
U kunt zien dat de toetsenbordknoppen vlaggen bijwerken om aan te geven of de links, omhoog, rechts of omlaag-toetsen worden ingedrukt. Deze vlaggen worden vastgelegd door de gebeurtenishandler enterframe en werken de grootte en richting van het schip bij.
Ik heb de vectorberekening van reflectievector in dit bericht al behandeld. Hier zal ik alleen bespreken hoe de normale vector uit gradiënt te verkrijgen.
\ [
\ Frac df (x) dx = gradiënt \\
regel \ gradient = \ frac y x \\
Stel \ gradient = 0,5 \\ in
y = 0,5 \\
x = 1 \\
Vector \ van \ left \ normal =
\ begin bmatrix -1 \\ 0.5 \ end bmatrix \\
Vector \ van \ recht \ normaal =
\ begin bmatrix 1 \\ - 0.5 \ end bmatrix
\]
Dus het onderstaande ActionScript zal het wiskundige concept implementeren dat in de vorige stap is uitgelegd. Bekijk de gemarkeerde lijnen:
private function bounce (): void var gradient: Number = quadratic_equation.diff1 (intersec_points [0] .x); var grad_vec: Vector2D = nieuwe Vector2D (1, verloop); var left_norm: Vector2D = grad_vec.getNormal (false); var right_norm: Vector2D = grad_vec.getNormal (); var chosen_vec: Vector2D; if (velo.dotProduct (left_norm)> 0) chosen_vec = left_norm else selected_vec = right_norm var selected_unit: Vector2D = chosen_vec.normalise (); var proj: Number = velo.dotProduct (selected_unit); chosen_unit.scale (-2 * proj); velo = velo.add (selected_unit);
Nou, bedankt voor je tijd! Als je dit nuttig hebt gevonden, of nog vragen hebt, laat dan een bericht achter.