Start | matris rotation3d
 

rotation i 3D



Rotera punkter runt origo, punkter som t.ex. kan representera hörn i en figur, en mesh.

matematiken

Så, matematiken för att rotera i 3 dimensioner är samma som rotation i 2D dimensioner, den enda skillnaden är att vi i våra matriser har ytterligare en rad och kolumn, vilket representerar ytterligare en dimension. Dessutom rotationsmatriser för 3D istället för 2D.

Vi tittar på koden för rotation i 2D bara för att kunna jämföra med koden som roterar i 3D precis nedanför denna kod.

rotation i 2 dimensioner


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["canvaz11"] 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)

rotation i 3 dimensioner


Så, vi fixar fram koordinaterna för hörnen i vår 3D -kub.
Nu roterar vi i 3D här nedan. Det som tillkommer är att vi skapar rotationsmatriser för 3D och väljer här nedan 2 av 3 möjliga axlar rotera runt. Det tillkommer också lite fler punkter för att skapa en kub och det tillkommer lite fler rader med multiplikationer. Annars är allt samma som med rotation i 2D.

Det är 100% medvetet att jag låtit koden nedan bara bygga vidare på rotation i 2D. Som du ser börjar det bli uppenbart att man kan optimera lite saker här. T.ex. skapa en lista av koordinater. Man kan också matematiskt optimera multiplikationerna. Mer om det längre fram.
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 rotationsmatris3DZ(z): R = [ [ 1, 0, 0], [ 0, math.cos(z), -math.sin(z)], [ 0, math.sin(z), math.cos(z)] ] return(Matrix(R)) def rotationsmatris3DY(y): R = [ [ math.cos(y), 0, math.sin(y)], [ 0, 1, 0], [-math.sin(y), 0, math.cos(y)] ] 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],[ 1]]) B = Matrix([[ 0],[ 1],[ 1]]) C = Matrix([[-1],[ 0],[ 1]]) D = Matrix([[ 0],[-1],[ 1]]) E = Matrix([[ 1],[ 0],[-1]]) F = Matrix([[ 0],[ 1],[-1]]) G = Matrix([[-1],[ 0],[-1]]) H = Matrix([[ 0],[-1],[-1]]) def rotera(): global A,B,C,D,E,F,G,H ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.beginPath() ctx.lineWidth=1 # Roterar våra koordinater ... R1 = rotationsmatris3DY(math.radians(5)) R2 = rotationsmatris3DZ(math.radians(3)) A = R1 @ R2 @ A B = R1 @ R2 @ B C = R1 @ R2 @ C D = R1 @ R2 @ D E = R1 @ R2 @ E F = R1 @ R2 @ F G = R1 @ R2 @ G H = R1 @ R2 @ H # Vi ritar kuben med A,B,C,D,E,F,G,H 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.moveTo(150-E.X*70, 150+E.Y*70) ctx.lineTo(150-F.X*70, 150+F.Y*70) ctx.lineTo(150-G.X*70, 150+G.Y*70) ctx.lineTo(150-H.X*70, 150+H.Y*70) ctx.lineTo(150-E.X*70, 150+E.Y*70) ctx.moveTo(150-A.X*70, 150+A.Y*70) ctx.lineTo(150-E.X*70, 150+E.Y*70) ctx.moveTo(150-B.X*70, 150+B.Y*70) ctx.lineTo(150-F.X*70, 150+F.Y*70) ctx.moveTo(150-C.X*70, 150+C.Y*70) ctx.lineTo(150-G.X*70, 150+G.Y*70) ctx.moveTo(150-D.X*70, 150+D.Y*70) ctx.lineTo(150-H.X*70, 150+H.Y*70) ctx.stroke() t = browser.timer.set_interval(rotera, 25) def clear(): browser.timer.clear_interval(t) browser.timer.set_timeout(clear, 15000)

projektion

Det kanske är lite baklänges -pedagogik här, men jag utelämnade förklara en viktig grej ovan.

Så, i ovan modell roterar vi punkter i 3 dimensioner. Men vilka punkter (som vi drar linjer mellan på skärmen) hamnar på vår 2D -canvas som vi tittar på? Vad är beräkningarna för detta? Svaret är att i enklaste fallet, vilket detta är, behövs inga beräkningar.

ortogonal projektion i enklaste fallet


Tänk dig en 3D -kub (i glas). Tänk dig att något roterar inne i kuben, som kuben ovan. En mindre kub roterar inuti en större kub, den stora kuben står still. Du kan vandra runt den stora kuben och titta på den lilla kuben som roterar där inne. I enklaste fallet tittar du in antingen från xy -sidan, xz -sidan eller yz -sidan. Tittar du från xy så är z djupet. Du ser inte djupet, du ser bara koordinaterma xy. Motsvarande resonemang på alla sidor. Enklaste formen av projektion, då tar vi våra koordinater (x,y,z) och vi tar helt enkelt bara bort en dimension så vi får kvar (x,y), sedan drar vi linjer på vår 2D -canvas mellan dessa (x,y) -punkter, där vi helt sonika bortser från z. Det är sedan vad du ser på skärmen. Vilken dimension vi tar bort beror på vilken sida vi står och tittar, vilket i ovan exempel inte spelar någon roll.

Som du ser om du tittar på klassen Matrix, så plockar vi bara X och Y vilka är de 2 första elementen i radvektorn som representerar punkten. Dvs, vi struntar i Z. Om du tvivlar, klicka på koden ovan, ändra sedan i klassen Matrix, så att du istället plockar X och Z eller Y och Z. Dvs, en snabb test ändra bara index, kör och se resultatet.

X och Z ...

    @property
    def X(self):
        return(self.I[0][0])
    @property    
    def Y(self):
        return(self.I[2][0])

Eller t.ex. Y och Z

    @property
    def X(self):
        return(self.I[1][0])
    @property    
    def Y(self):
        return(self.I[2][0])


15.075206756592 ms