Start | matris rotation2d
 

Rotation i 2D



Låt oss multiplicera lite matriser så att grejer börjar rotera.

rotationsmatris

Säger vi rotera en punkt eller rotera en vektor? Rent matematiskt roterar vi förstås vektor men vektorn i sin tur representerar i detta fall en punkt. Det är ju i slutändan punkten vi jobbar med.



Om vi tänker oss en geometrisk figur så består den av punkter mellan vilka vi drar linjer. Om vi flyttar punkterna, så flyttar vi figuren. Om vi flyttar punkterna, så att vi roterar dem alla runt origo, då kommer vår figur rotera runt origo. Vår punkt hamnar dock i ekvationerna som en radvektor och det är ju, om man ska vara petnoga, en vektor från origo till punkten ifråga. Men vi använder den som en punkt och spänner upp linjer mellan punkterna - inte längs vektorerna.

rotera en punkt runt origo


Vi kan enkelt rotera en vektor, som representerar en punkt, runt origo (0,0) genom att skapa en rotationsmatris1 i vilken vi stoppar in vinkeln vi vill rotera allting med. När vi skapat denna rotationsmatris, då tar vi denna matris och multiplicerar med vår punkt, beskriven som en radvektor. Ut kommer en ny punkt, som roterat så många grader, runt origo, som vi angav i vår roationsmatris.

1 Hur skapar vi den då? Det enkla svaret är att det finns en rotationsmatris för 2D och 3 stycken för 3D och det är bara att lära sig! Om du inte nöjer dig med det svaret, såhär kan du t.ex. härleda rotationsmatrisen för 2D.
  Visa lösning



Vi stoppar helt enkelt in önskad vinkel och rotationsmatrisen vi får, den multiplicerar vi med punkten vi vill rotera. Ut kommer punkten roterad. Genom att göra detta med en scens alla punkter, så roteras hela scenen.



exempel


I detta enkla inledande exempel ska vi bara rotera en enda punkt. Vi kan t.ex. välja punkten (x,y) = (1,0). Om vi tänker oss enhetscirkeln, så tänker vi oss att vi roterar punkten 90 grader. Var hamnar vi då? Vi borde ha flyttat punkten från (1,0) till (0,1). Eller hur?



Vi knappar ner lite pythonkod för detta. Den svåra biten, matrismultiplikation, den är ju redan avklarad. Så vi skapar en rotationsmatris och sedan multiplicerar vi den med en punkt, t.ex. (1,0) för att se om 90 grader rotation flyttar den till (0,1).
import math class Matrix: def __init__(self, I=None): self.I = I self.w = len(self.I[0]) self.h = len(self.I) def __str__(self): s = str(self.h)+'x'+str(self.w)+"\n" for i in range(self.h): for j in range(self.w): s = s + "{:>6}".format(round(self.I[i][j],1)) s = s + "\n" return(s) def __matmul__(self,B): C = [[0]*B.w for i in range(self.h)] for i in range(B.w): for j in range(self.h): for s in range(B.h): C[j][i]+= self.I[j][s] * B.I[s][i] return(Matrix(C)) def rotationsmatris2D(alfa): R = [ [math.cos(alfa), -math.sin(alfa)], [math.sin(alfa), math.cos(alfa)] ] return(Matrix(R)) A = Matrix([ [1], [0] ]) R = rotationsmatris2D(math.radians(90)) print("Rotationsmatris för +90 grader") print(R) print("Punkt innan rotation") print(A) B = R @ A print("Punkt efter rotation") print(B)
Nu är vi förstås nyfikna på hur det ser ut om vi ritar det i 2D och gör upprepande rotationer. Vi kopplar en timer till en funktion som ritar grafik på canvas under 10 sekunder...
import math from browser import document as doc import browser.timer class Matrix: def __init__(self, I=None): self.I = I self.w = len(self.I[0]) self.h = len(self.I) @property def X(self): return(self.I[0][0]) @property def Y(self): return(self.I[1][0]) def __matmul__(self,B): C = [[0]*B.w for i in range(self.h)] for i in range(B.w): for j in range(self.h): for s in range(B.h): C[j][i]+= self.I[j][s] * B.I[s][i] return(Matrix(C)) def rotationsmatris2D(alfa): R = [ [math.cos(alfa), -math.sin(alfa)], [math.sin(alfa), math.cos(alfa)] ] return(Matrix(R)) canvas = doc["canvaz12"] canvas.width = 250 canvas.height = 250 ctx = canvas.getContext('2d') ctx.strokeStyle = 'rgba(100, 255, 100, 1)' A = Matrix([ [1], [0] ]) def rotera(): global A ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.beginPath() ctx.lineWidth=3 # rita cirkel, origo 150,150 ctx.arc(150,150,75,0,2*math.pi); #roterar våra koordinater, 3 grader varje rotation R = rotationsmatris2D(math.radians(3)) A = R @ A # vi drar vår linje från origo 150,150 # till koordinaten vi beräknat ctx.moveTo(150, 150) ctx.lineTo(150-A.X*70, 150+A.Y*70) ctx.stroke() t = browser.timer.set_interval(rotera, 25) def clear(): browser.timer.clear_interval(t) browser.timer.set_timeout(clear, 10000)
Nu undrar du kanske varför det står ett minustecken framför A.X på raden lineTo och det beror på att canvas räknar x-axeln uppifrån och ner, dvs rotationen kommer bli åt fel håll om vi inte vänder på tecknet p.g.a. spegelvänt.

När vi ändå håller på och undrar en massa, behöver man verkligen linjär algebra för att få en visare att snurra runt moturs? Nej, verkligen inte. Vad linjär algebran gör, är i princip att trolla fram nedanstående lösning. Det är vad det kokar ner till om man gör matematiken.
import math from browser import document as doc import browser.timer canvas = doc["canvaz13"] canvas.width = 250 canvas.height = 250 ctx = canvas.getContext('2d') ctx.strokeStyle = 'rgba(100, 255, 100, 1)' vinkel = 0 def rotera(): global vinkel ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.beginPath() ctx.lineWidth=3 # rita cirkel, origo 150,150 ctx.arc(150,150,75,0,2*math.pi); # vi drar vår linje från origo 150,150 # till koordinaten vi nu beräknar med sinus och cosinus vinkel += math.radians(3) ctx.moveTo(150, 150) ctx.lineTo(150-math.cos(vinkel)*70, 150+math.sin(vinkel)*70) ctx.stroke() t = browser.timer.set_interval(rotera, 25) def clear(): browser.timer.clear_interval(t) browser.timer.set_timeout(clear, 10000)
Poängen med linjär algebra, det är att vi kan skapa en bild - eller snarare en mesh - av miljontals punkter. Sedan kan vi rotera alla dessa punkter. Det är lite jobbigt knappa in miljoner punkter, men vi kan utöka till 4 punkter och rita en kvadrat.
import math from browser import document as doc import browser.timer class Matrix: def __init__(self, I=None): self.I = I self.w = len(self.I[0]) self.h = len(self.I) @property def X(self): return(self.I[0][0]) @property def Y(self): return(self.I[1][0]) def __matmul__(self,B): C = [[0]*B.w for i in range(self.h)] for i in range(B.w): for j in range(self.h): for s in range(B.h): C[j][i]+= self.I[j][s] * B.I[s][i] return(Matrix(C)) def rotationsmatris2D(alfa): R = [ [math.cos(alfa), -math.sin(alfa)], [math.sin(alfa), math.cos(alfa)] ] return(Matrix(R)) canvas = doc["canvaz14"] canvas.width = 250 canvas.height = 250 ctx = canvas.getContext('2d') ctx.strokeStyle = 'rgba(100, 255, 100, 1)' A = Matrix([ [ 1], [ 0] ]) B = Matrix([ [ 0], [ 1] ]) C = Matrix([ [-1], [ 0] ]) D = Matrix([ [ 0], [-1] ]) def rotera(): global A,B,C,D ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.beginPath() ctx.lineWidth=3 # Roterar våra koordinater, 3 grader varje rotation R = rotationsmatris2D(math.radians(3)) A = R @ A B = R @ B C = R @ C D = R @ D # Vi ritar en kvadrat med A,B,C,D som hörn ctx.moveTo(150-A.X*70, 150+A.Y*70) ctx.lineTo(150-B.X*70, 150+B.Y*70) ctx.lineTo(150-C.X*70, 150+C.Y*70) ctx.lineTo(150-D.X*70, 150+D.Y*70) ctx.lineTo(150-A.X*70, 150+A.Y*70) ctx.stroke() t = browser.timer.set_interval(rotera, 25) def clear(): browser.timer.clear_interval(t) browser.timer.set_timeout(clear, 10000)
Låt oss gå över till rotation i 3D nu...
14.513969421387 ms