Gå til indholdet

Tutorial 4: Cylindriske og Sfæriske Koordinater i 3D

Når vi bevæger os fra 2D til 3D, udvides det polære koordinatsystem til to vigtige koordinatsystemer: cylindriske og sfæriske koordinater. Denne tutorial forklarer begge koordinatsystemer med en klar definition, praktiske eksempler, samt illustrerer hvordan de relaterer sig til det kartesiske koordinatsystem.

Cylindriske koordinater

Cylindriske koordinater er en direkte udvidelse af de 2D polære koordinater. Systemet består af tre koordinater:

  • \(r\): Afstanden fra z-aksen (radius).
  • \(\theta\): Vinklen i xy-planet fra den positive x-akse.
  • \(z\): Højden langs z-aksen, identisk med kartesisk z-koordinat.

Lokalisering af punkter:

  • Først findes positionen \((r, \theta)\) i xy-planet, præcis som med 2D polære koordinater.
  • Herefter bevæger man sig op eller ned langs z-aksen.

Konvertering til og fra kartesiske koordinater:

Fra cylindrisk til kartesisk:

\[ x = r\cos\theta,\quad y = r\sin\theta,\quad z = z \]

Fra kartesisk til cylindrisk:

\[ r = \sqrt{x^2 + y^2},\quad \theta = \text{atan2}(y, x),\quad z = z \]

💻 Konvertering i Python:

import math
import numpy as np

def cylindrical_to_cartesian(r, theta, z):
    """
    Konverterer cylindriske koordinater til kartesiske koordinater.

    Parametre:
        r (float): Radial afstand fra z-aksen
        theta (float): Vinkel i radianer i xy-planet
        z (float): Højde langs z-aksen

    Returnerer:
        tuple: (x, y, z) i kartesiske koordinater
    """
    x = r * math.cos(theta)
    y = r * math.sin(theta)
    # z ændres ikke
    return x, y, z

import math

def cartesian_to_cylindrical(x, y, z, output_degrees=False):
    """
    Konverterer kartesiske koordinater til cylindriske koordinater.

    Parametre:
        x (float): x-koordinat
        y (float): y-koordinat
        z (float): z-koordinat
        output_degrees (bool, optional): Hvis sandt, konverteres theta til grader.
            Standard er False, hvilket betyder at theta returneres i radianer.

    Returnerer:
        tuple: (r, theta, z) i cylindriske koordinater
    """
    r = math.sqrt(x*x + y*y)
    theta = math.atan2(y, x)

    # Konverter theta til grader hvis ønsket
    if output_degrees:
        theta = math.degrees(theta)

    # z forbliver uændret
    return r, theta, z


# Eksempel på anvendelse
if __name__ == "__main__":
    # Eksempel på punkt i cylindriske koordinater
    r, theta, z = 5, math.radians(45), 10

    # Konverter til kartesiske koordinater
    x, y, z_cart = cylindrical_to_cartesian(r, theta, z)
    print(f"Kartesiske koordinater: x={x}, y={y}, z={z_cart}")

    # Konverter tilbage til cylindriske koordinater
    r_cyl, theta_cyl, z_cyl = cartesian_to_cylindrical(x, y, z_cart)
    print(f"Tilbage til cylindriske: r={r_cyl}, theta={math.degrees(theta_cyl)}°, z={z_cyl}")

Praktisk anvendelse:

Cylindriske koordinater er især nyttige ved cylindriske objekter og rotationer om en akse, fx rør, spiraler eller rotationssymmetriske genstande.

Sfæriske koordinater

Sfæriske koordinater beskriver punkter ved hjælp af en afstand og to vinkler, hvilket gør dem særligt nyttige ved kugleformet geometri, eksempelvis planeter, kameraorientering, og lydbølger.

Matematisk konvention:

  • \(r\) (radius): Afstanden fra origo til punktet.
  • \(\theta\) (azimut): Vinkel fra den positive x-akse i xy-planet.
  • \(\phi\) (zenith): Vinkel målt fra den positive z-akse nedad (0° peger lodret op, 180° peger nedad).

Lokalisering af punkter:

  • Drej først vinklen \(\theta\) om z-aksen.
  • Drej herefter nedad med vinklen \(\phi\) fra z-aksen.
  • Bevæg dig til sidst ud langs radius \(r\).

Konvertering mellem sfæriske og kartesiske koordinater:

  • Sfærisk til kartesisk:

    • \(x = r \sin\phi \cos\theta\)
    • \(y = r \sin\phi \sin\theta\)
    • \(z = r \cos\phi\)
  • Kartesisk til sfærisk:

    • \(r = \sqrt{x^2 + y^2 + z^2}\)
    • \(\theta = \text{atan2}(y, x)\)
    • \(\phi = \arccos\left(\frac{z}{r}\right)\)

💻 Konvertering i Python:

import math
import numpy as np

def spherical_to_cartesian_math(r, theta, phi):
    """
    Konverterer sfæriske koordinater til kartesiske koordinater.

    Parametre:
    r (float): Radius (afstand fra origo)
    theta (float): Azimuthal vinkel i radianer (vinkel i xy-planen)
    phi (float): Polar vinkel i radianer (vinkel fra z-aksen)


    Returnerer:
    tuple: (x, y, z) i kartesiske koordinater
    """
    x = r * math.sin(phi) * math.cos(theta)
    y = r * math.sin(phi) * math.sin(theta)
    z = r * math.cos(phi)
    return (x, y, z)

import math

def cartesian_to_spherical_math(x, y, z):
    """
    Konverterer kartesiske koordinater til sfæriske koordinater (matematisk konvention).

    Parametre:
    x (float): x-koordinat
    y (float): y-koordinat
    z (float): z-koordinat

    Returnerer:
    tuple: (r, theta, phi) hvor:
        - r (float): Afstanden fra origo.
        - theta (float): Azimuthal vinkel i radianer (vinkel i xy-planen, målt fra den positive x-akse).
        - phi (float): Polar vinkel i radianer (vinkel fra z-aksen).
    """
    r = math.sqrt(x*x + y*y + z*z)

    # Håndterer specialtilfælde for at undgå division med nul
    if r == 0:
        return 0, 0, 0

    theta = math.atan2(y, x)
    phi = math.acos(z / r)

    return r, theta, phi

Videospil-konvention:

I spiludvikling anvendes ofte en anden intuitiv konvention:

  • \(r\) (radius): Afstanden til punktet.
  • \(h\) (heading): Horisontal vinkel fra den positive z-akse (fremad/nord).
  • \(p\) (pitch): Vertikal vinkel fra vandret plan; positiv nedad, negativ opad.

Denne konvention er ofte mere intuitiv ved kamerastyring i spil.

Aliasing og Gimbal lock i Sfæriske Koordinater

Sfæriske koordinater har også aliasing udfordringer, fordi forskellige koordinater kan beskrive samme punkt:

  • Aliasing: Opstår grundet periodiciteten af vinkler.
  • Gimbal lock: Opstår ved pitch-vinkler tæt på ±90°, hvor heading-vinklen mister betydning.

For at undgå dette anvendes også her en kanonisk form for koordinaterne:

  • Begrænsninger (videospil-konvention):
  • \(r \geq 0\)
  • \(-180^\circ < h \leq 180^\circ\)
  • \(-90^\circ \leq p \leq 90^\circ\)
  • Ved \(r = 0\): sæt \(h = 0\), \(p = 0\)
  • Ved \(|p| = 90^\circ\): sæt \(h = 0\)

💻 Kanonisering (Python):

def strict_canonical_spherical(r, h, p):
    """
    Følger strengt den 7-trins algoritme for kanonisk form-konvertering
    som beskrevet i bogen.
    """
    # Konverter til grader for nemmere at følge algoritmen
    p_deg = math.degrees(p)
    h_deg = math.degrees(h)

    # 1. Hvis r=0, så sæt h=p=0
    if r == 0:
        return 0, 0, 0

    # 2. Hvis r<0, så negér r, læg 180° til h, og negér p
    if r < 0:
        r = -r
        h_deg += 180
        p_deg = -p_deg

    # 3. Hvis p<-90°, så læg 360° til p indtil p≥-90°
    while p_deg < -90:
        p_deg += 360

    # 4. Hvis p>270°, så træk 360° fra p indtil p≤270°
    while p_deg > 270:
        p_deg -= 360

    # 5. Hvis p>90°, så læg 180° til h og sæt p=180°-p
    if p_deg > 90:
        h_deg += 180
        p_deg = 180 - p_deg

    # 6. Hvis h≤-180°, så læg 360° til h indtil h>-180°
    while h_deg <= -180:
        h_deg += 360

    # 7. Hvis h>180°, så træk 360° fra h indtil h≤180°
    while h_deg > 180:
        h_deg -= 360

    # Konverter tilbage til radianer
    h = math.radians(h_deg)
    p = math.radians(p_deg)

    return r, h, p

💻 Konvertering mellem sfæriske og kartesiske koordinater (videospil):

Givet at du har de tre koordinater \(r\), \(h\) og \(p\), kan du konvertere mellem sfæriske og kartesiske koordinater som følger:

  • Sfærisk til kartesisk:

    • \(x = r \cos p \sin h\)
    • \(y = -r \sin p\)
    • \(z = r \cos p \cos h\)
  • Kartesisk til sfærisk:

    • \(r = \sqrt{x^2 + y^2 + z^2}\)
    • \(h = \text{atan2}(x, z)\)
    • \(p = \arcsin\left(-\frac{y}{r}\right)\)
import math

def spherical_to_cartesian_game(r, h, p):
   """
   Konverterer sfæriske koordinater til kartesiske koordinater.

   Parametre:
   r (float): Radius (afstand fra origo)
   h (float): Horisontal vinkel fra den positive z-akse (fremad/nord).
   theta (float): Vertikal vinkel fra vandret plan; positiv nedad, negativ opad.

   Returnerer:
   tuple: (x, y, z) i kartesiske koordinater
   """
   x = r * math.cos(p) * math.sin(h)
   y = -r * math.sin(p)
   z = r * math.cos(p) * math.cos(h)
   return x, y, z

def cartesian_to_spherical_game(x, y, z):
   """
   Konverterer kartesiske koordinater til sfæriske koordinater.

   Parametre:
   x (float): x-koordinat
   y (float): y-koordinat
   z (float): z-koordinat

   Returnerer:
   tuple: (r, h, p) i sfæriske (kanoniske) game-koordinater
   """
   r = math.sqrt(x*x + y*y + z*z)

   # Håndterer specialtilfælde for at undgå division med nul
   if r == 0:
       return 0, 0, 0

   h = math.atan2(x, z)
   p = math.asin(- y / r)


   return r, h, p