decocode decocode deco    

Geometrische Funktionen #

Übersicht
Berechnung von Koordinaten
Schnittpunkte ermitteln
Prüfen, ob sich ein Punkt in einem Dreieck
   befindet

Seiten eines beliebigen Dreiecks berechnen
Fläche eines beliebigen Dreiecks berechnen
Pfad in Dreicke zerlegen
Beliebige Flächen bestimmen

Kurvenverlauf einer Ellipse
Position eines Objekts auf einer Keplerbahn berechnen
Äquatoriale und horizontale Koordinaten berechnen
Winkel umrechnen

Achteck in einem Quadrat
Höhe über einer Seite eines beliebigen Dreiecks
Länge der dritten Seite eines beliebigen Dreiecks
Länge der zweiten Seite eines beliebigen Dreiecks
Winkel in einem beliebigen Dreieck

PHP bietet praktisch für alle gängigen geometrischen Aufgaben entsprechende trigonometrische Funktionen zur Berechnung von Winkeln und Strecken. Die wichtigsten sind:

sin() und asin() - Sinus und Arkussinus
cos() und acos() - Kosinus und Arkuskosinus
tan() und atan() - Tangens und Arkustangens
hypot() - Länge einer Hypotenuse
pi() - die Kreiszahl π

Bei der Verwendung dieser Funktionen ist zu beachten, dass Winkel nicht in Grad, sondern in Radiant angegeben werden. Das heißt, der Vollkreis von 360° beträgt 2 π (ca. 6,28), der Halbkreis von 180° entsprechend 1 π (ca. 3,14) und der Viertelkreis von 90° demnach π / 2 (ca. 1,57). Für die Umrechnung stehen folgende Funktionen zur Verfügung:

deg2rad() - Umrechnung von Grad in Radiant
rad2deg() - Umrechnung von Radiant in Grad

Außerdem sollte man das Problem der Nichtumwandelbarkeit vieler Gleitkommazahlen in Binärzahlen bedenken, die insbesondere bei Vergleichsoperationen zu unerwarteten Ergebnissen führen kann:

cos(pi() / 2) ergibt 6.1232339957368E-17 statt 0.
tan(pi()) ergibt -1.2246467991474E-16 statt 0.
tan(pi() / 2) ergibt 1.6331239353195E+16 statt usw.

Häufig benötigt man zusätzlich folgende Funktionen:

pow() - Potenz
sqrt() - Quadratwurzel

Die hier vorgestellten Funktionen greifen gelegentlich auf andere Funktionen zurück, die an anderer Stelle auf dieser Seite erläutert werden. Um einzelne Funktionen verwenden zu können, müssen daher auch die anderen benötigten Funktionen zur Verfügung stehen!

Berechnung von Koordinaten #

Innerhalb eines Koordinatensystems wird die Beziehung zwischen einzelnen Punkten im Wesentlichen durch ihren Abstand zueinander und durch den Winkel zwischen einer imaginären Verbindungslinie und einer Bezugsachse bestimmt.

In diesem Beispiel haben wir ein Koordinatensystem, bei dem die horizontale Position nach rechts und die vertikale Position nach unten hin zunimmt. Der Nullpunkt befindet sich also in der oberen linken Ecke.

P₀ P₁ dx dy c α₁ β₁ α₂ β₂ P₀ (5;6) P₁ (1;4) P₂ (9;4) P₃ (9;8)

Der diagonale Abstand der Punkte zueinander $c kann mit Hilfe des Satzes des Pythagoras berechnet werden. Der horizontale Abstand $dx und der vertikale Abstand $dy errechnen sich aus der Differenz der jeweiligen Koordinaten.

$c = hypot($dx, $dy); bzw.
$c = sqrt(pow($dx, 2) + pow($dy, 2));

Die Winkel können durch Arkussinus- bzw. Arkuskosinusberechnung ermittelt werden.

Über den Arkussinus lassen sich die Winkel zwischen der Hypotenuse $c und den Ankatheten des Dreiecks zwischen zwei Punkten bestimmen. Der Wert liegt hier immer zwischen –90° und +90°, wobei das Vorzeichen angibt, ob sich der Punkt P₁ links oder rechts bzw. ober- oder unterhalb von P₀ befindet, je nachdem, ob der Arkussinus auf $dx für den Winkel α₁ oder auf $dy für den Winkel β₁ angewendet wurde. Für eine eindeutige Bestimmung der Position von P₁ ist die Berechnung eines einzelnen Winkels über den Arkussinus nicht ausreichend, da im Beispiel der Winkel α₁ der Punkte P₂ und P₃ sowie der Winkel β₁ der Punkte P₁ und P₂ den gleichen Wert haben.

Über den Arkuskosinus lassen sich die Winkel zwischen der Hypotenuse $c und dem positiven Abschnitt der Achsen, die durch den Punkt P₀ laufen, bestimmen. Der Wert liegt hier immer zwischen 0° und 180°, wodurch sich anhand des Winkels feststellen lässt, ob sich der Punkt P₁ links oder rechts bzw. ober- oder unterhalb von P₀ befindet, je nachdem, ob der Arkuskosinus auf $dx für den Winkel β₂ oder auf $dy für den Winkel α₂ angewendet wurde. Für eine eindeutige Bestimmung der Position von P₁ ist die Berechnung eines einzelnen Winkels über den Arkuskosinus nicht ausreichend, da im Beispiel der Winkel α₂ der Punkte P₁ und P₂ sowie der Winkel β₂ der Punkte P₂ und P₃ den gleichen Wert haben.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  $points = [
    [5,6],  # P0
    [1,4],  # P1
    [9,4],  # P2
    [9,8],  # P3
  ];
  
  for ($i = 1; $i < count($points); $i++) {
    $dx = $points[$i][0] - $points[0][0];
    $dy = $points[$i][1] - $points[0][1];
    $c  = hypot($dx, $dy);  # == sqrt(pow($dx, 2) + pow($dy, 2));

    if ($c != 0) {
      $alpha1 = asin($dx / $c);
      $beta1  = asin($dy / $c);
      $alpha2 = acos($dy / $c);
      $beta2  = acos($dx / $c);
    } else $alpha1 = $beta1 = $alpha2 = $beta2 = 0;

    echo "P".$i.": ";
    echo "c = ".$c."; ";
    echo "α₁ = ".rad2deg($alpha1)."; ";
    echo "β₁ = ".rad2deg($beta1)."; ";
    echo "α₂ = ".rad2deg($alpha2)."; ";
    echo "β₂ = ".rad2deg($beta2)."<br>\r\n";
  }

Wie man sieht, werden auf diese Weise für die Beschreibung des Verhältnisses zwischen zwei Punkten im Koordinatensystem immer drei Parameter benötigt: der Abstand $c sowie zwei Winkel, die über den Arkussinus oder den Arkuskosinus bestimmt wurden.

Einfacher wäre es dagegen, wenn das Verhältnis der Punkte lediglich durch ihren Abstand zueinander sowie einen einzigen Winkel des Dreiecks zwischen ihnen beschrieben werden könnte, wobei dieser Winkel einen Wert zwischen 0° und 360° annehmen können müsste.

P₀ P₁ dx dy c α₁ β₁ α₂ β₂ P₀ P₁ dx dy c δ

Die Funktion coord_props() implementiert diese Anforderung. Sie erwartet den Ausgangs- sowie den Zielpunkt als Argumente $p0 und $p1 und gibt ein Array zurück, das den Abstand der Punkte und den Winkel δ enthält, der rechtsdrehend von dem negativen Abschnitt der senkrechten Achse gemessen wird, die durch den Punkt P₀ verläuft.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  function coord_props($p0, $p1) {
    $dx = $p1[0] - $p0[0];
    $dy = $p1[1] - $p0[1];
    $c = hypot($dx, $dy);
    if ($c != 0) {
      $alpha = asin($dx / $c);
      $delta = ($dy <  0) * (($dx < 0) * 2 * pi() + $alpha) +
               ($dy >= 0) * (pi() - $alpha);
    } else $delta = FALSE;
    return [$c, $delta];
  }

  $points = [
    [5,6],  # P0
    [1,4],  # P1
    [9,4],  # P2
    [9,8],  # P3
  ];
  
  for ($i = 1; $i < count($points); $i++) {
    $props = coord_props($points[0], $points[$i]);
    $c     = $props[0];
    $delta = $props[1];

    echo "P".$i.": ";
    echo "c = ".$c."; ";
    echo "δ = ".rad2deg($delta)."<br>\r\n";
  }

Mit der folgenden Funktion coord_pos() lassen sich nun umgekehrt die Koordinaten eines Punktes ermitteln, der sich zu einem gegebenen Punkt $p0 im Abstand $c mit dem Winkel $delta befindet.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  function coord_pos($p0, $c, $delta) {
    $x = $p0[0] + sin($delta) * $c;
    $y = $p0[1] - cos($delta) * $c;
    return [$x, $y];
  }

  $p0 = [0,0];
  $c = 4;

  for ($i = 0; $i < 8; $i++) {
    $delta = deg2rad($i * 45);
    
    $pos = coord_pos($p0, $c, $delta);
    $x = number_format($pos[0], 2, ".", "");
    $y = number_format($pos[1], 2, ".", "");
    
    echo "P".($i + 1)." [".$x.";".$y."]: ";
    echo "c = ".$c."; ";
    echo "δ = ".rad2deg($delta)."<br>\r\n";
  }

Schnittpunkte ermitteln #

Unter Umständen möchte man feststellen, ob und an welchem Punkt sich die Achsen zweier Strecken schneiden und ob sich auch die Strecken selbst schneiden.

P₀ P₁ P₂ P₃ P₄ P₅ S₂ S₀

Die folgende Funktion coord_intersect() erwartet dazu zwei Wertepaare, die aus den Koordinaten der Endpunkte zweier zu vergleichender Strecken bestehen. Je nach Konstellation der Linien zueinander werden verschiedene Rückgabewerte generiert (s. Beispiel).

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  function coord_round($n) {
    # normalizes numbers to buffer floating point problem
    # you can lower the sensitivity by decreasing argument #2 to get rougher results
    return round($n, 12);
  }

  function coord_round_pos($pos) {
    # normalizes coordinates to buffer floating point problem
    return [coord_round($pos[0]), coord_round($pos[1])];
  }

  function coord_positive_angle($alpha) {
    # converts negative angles to positive
    if ($alpha < 0) return ($alpha + 2 * pi()); else return $alpha;
  }

  function coord_sign($n) {
    # returns the sign of a number
    if ($n < 0) return -1; else return 1;
  }
  
  function coord_intersect($l0, $l1) {
    $props0 = coord_props($l0[0], $l0[1]);
    $props1 = coord_props($l1[0], $l1[1]);
    
    if (coord_round($props0[0]) == 0 || coord_round($props1[0]) == 0) {  # one line has zero length
      $ret0 = 0;
      $ret1 = 0;
    } elseif (
      coord_round($props0[1]) == coord_round($props1[1]) ||
      coord_round($props0[1]) == coord_round($props1[1] - pi()) ||
      coord_round($props1[1]) == coord_round($props0[1] - pi())
    ) {  # lines are parallel
      
        $props = coord_props($l0[0], $l1[0]);
        if (
          coord_round($props[1]) == coord_round($props0[1]) ||
          coord_round($props[1]) == coord_round(coord_positive_angle($props0[1] - pi())) ||
          $props[0] == 0
        ) {  # lines share the same axis
          $ret0 = 2;
          $ret1 = 0;
        } else {  # lines have different axes
          $ret0 = 1;
          $ret1 = 0;
        }
      
    } else {  # axes intersect
      
      $dx = $l1[0][0] - $l0[0][0];
      $dy = $l1[0][1] - $l0[0][1];
      
      $alpha = $props0[1] - 0.5 * pi();
      $beta  = 1.5 * pi() - $props1[1];
      
      if (coord_round(sin($beta)) == 0) {
        $gamma = pi() - $props0[1];
        $c0 = $dy / cos($gamma);  # result of cos() is never 0
        $c1 = ($dx - sin($gamma) * $c0) * -1 * coord_sign($l1[1][0] - $l1[0][0]);
        $edges = [$c0, $c1];
        $pos = $l1[0];
      } else {
        $c = $dy / sin($beta);
        $edges = coord_edges($dx + cos($beta) * $c, $alpha, $beta);
        $pos = coord_pos($l1[0], $c * -1,   $props1[1]);
      }
      
      $s0 = coord_pos($l0[0], $edges[0], $props0[1]);
      $s1 = coord_pos($pos,   $edges[1], $props1[1]);  # must be equal to $s0
      
      $ret1 = $s0;
      
      # check if lines intersect
      $d00 = coord_props($l0[0], $s0);
      $d01 = coord_props($l0[1], $s0);
      $d10 = coord_props($l1[0], $s0);
      $d11 = coord_props($l1[1], $s0);
      
      if (  # V intersection
        coord_round_pos($l0[0]) == coord_round_pos($l1[0]) ||
        coord_round_pos($l0[0]) == coord_round_pos($l1[1]) ||
        coord_round_pos($l0[1]) == coord_round_pos($l1[0]) ||
        coord_round_pos($l0[1]) == coord_round_pos($l1[1])
      ) $ret0 = 4;
      elseif (  # T intersection
        (
          coord_round($d00[1]) != coord_round($d01[1]) &&
          (coord_round_pos($l1[0]) == coord_round_pos($s0) || coord_round_pos($l1[1]) == coord_round_pos($s0))
        ) ||
        (
          coord_round($d10[1]) != coord_round($d11[1]) &&
          (coord_round_pos($l0[0]) == coord_round_pos($s0) || coord_round_pos($l0[1]) == coord_round_pos($s0))
        )
      ) $ret0 = 5;
      elseif (  # X intersection
        coord_round($d00[1]) != coord_round($d01[1]) &&
        coord_round($d10[1]) != coord_round($d11[1])
      ) $ret0 = 6;
      else $ret0 = 3;  # no intersection
      
    }
    
    return [$ret0, $ret1];
  }
  
  $lines = [
    [
      [2,1],
      [3,5],
    ],
    [
      [9,3],
      [6,6],
    ],
  );
  
  $sect = coord_intersect($lines[0], $lines[1]);
  if ($sect[0] > 2) {
    echo "Schnittpunkt: [".$sect[0][0].";".$sect[0][1]."]; ";
    if ($sect[0] == 6) echo "schneiden sich";
    elseif ($sect[0] == 5) echo "berühren sich T-Form";
    elseif ($sect[0] == 4) echo "berühren sich V-Form";
    else echo "schneiden sich nicht";
  } elseif ($sect[0] == 2) {
    echo "fluchtend";
  } elseif ($sect[0] == 1) {
    echo "parallel";
  } else $text = "keine Linie";

Und hier der Code in Aktion:

Prüfen, ob sich ein Punkt in einem Dreieck befindet #

Um festzustellen, ob sich ein Punkt innerhalb eines gegebenen Dreiecks befindet, muss überprüft werden, ob alle Winkel zwischen dem erwähnten Punkt und den drei Eckpunkten des Dreiecks kleiner als 180° bzw. π sind. Ist dies nicht der Fall, so befindet sich der Punkt außerhalb des Dreiecks.

T₀ T₁ T₂ P₀ α β γ T₀ T₁ T₂ P₁ α β γ

Die folgende Funktion coord_is_on_triangle() erwartet dazu als Argumente die Koordinaten des betreffenden Punktes und der Eckpunkte des Dreiecks innerhalb eines Arrays. Rückgabewert ist 2, wenn sich der Punkt im Dreieck befindet. Liegt der Punkt direkt auf der Verbindungslinie der Eckpunkte, wird 1 zurückgegeben, ansonsten 0.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  function coord_positive_angle($alpha) {
    # converts negative angles to positive
    if ($alpha < 0) return ($alpha + 2 * pi()); else return $alpha;
  }

  function coord_is_on_triangle($p0, $tri) {
    # checks if point $p0 is part of triangle $tri
    $a = [];
    foreach ($tri as $p1) {
      $props = coord_props($p0, $p1);
      $a[] = $props[1];
    }
    reset($tri);
    # the amount of negative angles depends on the direction of the points in the triangle
    $negative = 0;
    $alpha = $a[1] - $a[0];
    if ($alpha < 0) $negative++;
    $beta  = $a[2] - $a[1];
    if ($beta  < 0) $negative++;
    $gamma = $a[0] - $a[2];
    if ($gamma < 0) $negative++;
    if ($negative == 2) $sg = -1; else $sg = 1;
    $alpha = coord_positive_angle($alpha * $sg);
    $beta  = coord_positive_angle($beta  * $sg);
    $gamma = coord_positive_angle($gamma * $sg);
    # if any of the three angles between point $p0 and the triangle corners is > 180° $p0 is outside the triangle
    if     ($alpha <  pi() && $beta <  pi() && $gamma <  pi()) return 2;
    elseif ($alpha == pi() && $beta == pi() && $gamma == pi()) return 1;
    else return 0;
  }
  
  $points = [
    [5,6],  # P0
    [8,3],  # P1
  ];
  
  $tri = [
    [4,1],  # T0
    [2,8],  # T1
    [9,9],  # T2
  ];
  
  for($i = 0; $i < count($points); $i++) {
    echo "Punkt P".$i." [".$points[$i][0].";".$points[$i][1]."] befindet sich ";
    if (coord_is_on_triangle($points[$i], $tri) == TRUE) echo "innerhalb"; else echo "außerhalb";
    echo " des Dreiecks.<br>\r\n";
  }
  reset($points);

Seiten eines beliebigen Dreiecks berechnen #

Sind die Länge einer Seite und die angrenzenden Winkel bekannt, so können die Längen der übrigen Seiten errechnet werden, indem man das Dreieck an der Höhe über einer gesuchten Seite teilt und die Eigenschaften der Teildreiecke berechnet.

a α β b c b₂ b₁ hb

Die Funktion coord_edges() erwartete als Argumente dazu die Länge der bekannten Seite a und die Größen der angrenzenden Winkel α und β. Der Rückgabewert ist ein Array mit den Längen der Seiten b und c.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  function coord_edges($a, $alpha, $beta) {
    # calculates the lengths of the other edges of a triangle with edge length $a and $alpha and $beta to be the angles next to $a.
    $hb = sin($alpha) * $a;
    $gamma = pi() - $alpha - $beta;
    $c = $hb / sin($gamma);
    $b1 = $a * cos($alpha);
    $b2 = $c * cos($gamma);
    $b = $b1 + $b2;
    return [$b, $c];
  }

  $edges = coord_edges(12.5, deg2rad(58), deg2rad(64));
  echo "b = ".$edges[0]."<br>";
  echo "c = ".$edges[1]."<br>";

Fläche eines beliebigen Dreiecks berechnen #

Die Fläche eines beliebigen Dreiecks, dessen Seitenlängen bekannt sind, errechnet sich relativ einfach aus der Hälfte des Produktes aus einer Seitenlänge und der Höhe auf diese Seite:

Formel 1

Die Höhe errechnet sich nach folgender Formel aus dem Pythagorassatz:

Formel 2

Der Abschnitt c1 errechnet sich nach dieser Formel:

Formel 3

T₀ T₁ T₂ a b c c₁ c₂ hc

Dazu erwartet die folgende Formel coord_triangle_area() ein Array, das die drei Wertepaare der Eckpunktkoordinaten des Dreiecks enthält. Der Rückgabewert ist die Fläche des Dreiecks.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  function coord_triangle_area($tri) {
    # calculates the area of an arbitrary triangle between given points
    $props0 = coord_props($tri[0], $tri[1]);
    $props1 = coord_props($tri[1], $tri[2]);
    $props2 = coord_props($tri[2], $tri[0]);
    $a = $props0[0];
    $b = $props1[0];
    $c = $props2[0];
    if (coord_round($a) != 0 && coord_round($b) != 0 && coord_round($c) != 0) {
      $c1 = (pow($a, 2) - pow($b, 2) - pow($c, 2)) / (-2 * $c);
      $hc1 = sqrt(pow($b, 2) - pow($c1, 2));
      # just for the record, hc1 and hc2 should be equal:
      # $c2 = $c - $c1;
      # $hc2 = sqrt(pow($a, 2) - pow($c2, 2));
      $area = $c * $hc1 / 2;
      #echo "<!-- ".$c1." -->\r\n";
    } else $area = 0;
    return $area;
  }

  $tri = [
    [4,1],  # T0
    [2,8],  # T1
    [9,9],  # T2
  ];
  
  echo "Fläche = ".coord_triangle_area($tri);

Pfad in Dreicke zerlegen #

Um den Flächeninhalt eines beliebigen Pfades zu ermitteln, kann man die Fläche dieses Pfades in einzelne Dreiecke zerlegen und deren Flächeninhalte addieren. Für präzise Ergebnisse darf der Pfad daher ausschließlich aus geraden Linien bestehen.

Dazu erwartet die Formel coord_triangles_from_path als erstes Argument ein Array, das die Wertepaare der Eckpunktkoordinaten des Pfades im Uhrzeigersinn enthält. Als zweites Argument kann für experimentelle Zwecke der Index des Startpunktes angegeben werden, ab dem mit der Suche nach Dreiecken begonnen wird. Der Rückgabewert sollte aber unabhängig vom Startpunkt sein, ansonsten wäre das ein Hinweis auf einen Bug im Skript. Der Rückgabewert ist ein Array, das die Eckpunkte der gefundenen Dreiecke enthält.

Diese Dreiecke können dann im Anschluss vermessen werden. Die Flächensumme der Dreiecke sollte der Gesamtfläche des Pfades entsprechen.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  function coord_compare_triangle($tri0, $tri1) {
    # compares if two triangles share the same points
    if (
      count(array_keys($tri0, $tri0[0], TRUE)) == count(array_keys($tri1, $tri0[0], TRUE)) &&
      count(array_keys($tri0, $tri0[1], TRUE)) == count(array_keys($tri1, $tri0[1], TRUE)) &&
      count(array_keys($tri0, $tri0[2], TRUE)) == count(array_keys($tri1, $tri0[2], TRUE))
    ) return TRUE; else return FALSE;
  }
  
  function coord_triangle_exists($t, $tris) {
    # checks if array with points of triangle $tri exists in array $tris (insensitive to order of points)
    foreach($tris as $tri) {
      if (coord_compare_triangle($t, $tri) == TRUE) {
        return TRUE;
      }
    }
    reset($tris);
    return FALSE;
  }
  
  function coord_check_intersect($points, $lines, $start, $end) {
    # checks if the line from start to end intersects any lines in the path of $points
    $ret = FALSE;
    $count = count($points);
    for ($i = 0; $i < $count; $i++) {
      $sect = coord_intersect(
        [$points[$i], $points[coord_increase($i, $count)]],
        [$points[$start], $points[$end]]
      );
      if ($sect[0] == 6) $ret = TRUE;
    }
    $count = count($lines);
    for ($i = 0; $i < $count; $i++) {
      $sect = coord_intersect(
        [$points[$lines[$i][0]], $points[$lines[$i][1]]],
        [$points[$start], $points[$end]]
      );
      if (
        !in_array([$start, $end], $lines) &&
        !in_array([$end, $start], $lines) &&
        $sect[0] == 6
      ) $ret = TRUE;
    }
    return $ret;
  }
  
  function coord_check_outside($points, $start, $end) {
    # checks if line from start to end crosses outside the path of points
    $prev = coord_decrease($start, count($points));
    $next = coord_increase($start, count($points));
    $alpha = coord_props($points[$start], $points[$prev]);
    $alpha = $alpha[1];
    $beta  = coord_props($points[$start], $points[$next]);
    $beta  = $beta[1];
    $gamma = coord_props($points[$start], $points[$end]);
    $gamma = $gamma[1];
    if (coord_positive_angle($gamma - $beta) > coord_positive_angle($alpha - $beta)) return TRUE; else return FALSE;
  }
  
  function coord_increase($val, $n) {
    if ($val != $n - 1) $val++; else $val = 0;
    return $val;
  }
  
  function coord_decrease($val, $n) {
    if ($val != 0) $val--; else $val = $n - 1;
    return $val;
  }
  
  function coord_triangles_from_path($points, $p0 = 0) {
    $tris = $lines = [];
    $c = count($points);
    if (!is_numeric($p0) || $p0 < 0 || $p0 >= $c) $p0 = 0;
    
    # step 1: find triangles along the path of $points
    $stop1 = coord_decrease($p0, $c);
    do {  # loop over all startpoints
      $p1 = coord_increase($p0, $c);
      $p2 = coord_increase($p1, $c);
      $stop2 = coord_decrease($p0, $c);
      do {  # loop over all endpoints
        if (
          !coord_check_outside($points, $p0, $p1) &&
          !coord_check_outside($points, $p0, $p2) &&
          !coord_check_intersect($points, $lines, $p0, $p1) &&
          !coord_check_intersect($points, $lines, $p0, $p2) &&
          !coord_triangle_exists([$p0, $p2, $p1], $tris) &&
           coord_triangle_area([$points[$p0], $points[$p2], $points[$p1]]) > 0
        ) {
          $tris[]  = [$p0, $p2, $p1];
          $lines[] = [$p0, $p1];
          $lines[] = [$p0, $p2];
        }
        $p1 = coord_increase($p1, $c);
        $p2 = coord_increase($p1, $c);
      } while($p1 != $stop2);
      
      $p0 = coord_increase($p0, $c);
    } while($p0 != $stop1);
    
    # step 2: find remaining internal triangles
    $p0 = coord_increase($p0, $c);
    $stop1 = $p0;
    do {  # loop over all points
      $ends = [];
      foreach($lines as $line) {  # find all lines in $lines that start at current point
        $prev = coord_decrease($p0, $c);
        $next = coord_increase($p0, $c);
        if (
          $line[0] == $p0 &&
          $line[1] != $prev &&
          $line[1] != $next &&
          !in_array($line[1], $ends)
        ) $ends[] = $line[1]; elseif (
          $line[1] == $p0 &&
          $line[0] != $prev &&
          $line[0] != $next &&
          !in_array($line[0], $ends)
        ) $ends[] = $line[0];
      }
      reset($lines);
      for($i = 0; $i < count($ends); $i++) {  # loop over these lines
        for($j = 0; $j < count($ends); $j++) {
          if ($i != $j) {
            $tri = [$p0, $ends[$i], $ends[$j]];
            if (
              !coord_triangle_exists($tri, $tris) &&
              !coord_check_outside($points, $ends[$i], $ends[$j]) &&
              !coord_check_intersect($points, $lines, $ends[$i], $ends[$j]) &&
               coord_triangle_area([$points[$p0], $points[$ends[$i]], $points[$ends[$j]]]) > 0
            ) {
              $tris[]  = $tri;
              $lines[] = [$ends[$i], $ends[$j]];
            }
          }
        }
      }
      $p0 = coord_increase($p0, $c);
    } while ($p0 != $stop1);
    
    return $tris;
  }
  
  $points = [
    [1,4],
    [5,2],
    [12,3],
    [6,13],
    [12,12],
    [10,10],
    [18,7],
    [18,14],
    [14,18],
    [3,16],
  );
  
  $area = 0;
  $tris = coord_triangles_from_path($points);
  for($i = 0; $i < count($tris); $i++) {
    $area += coord_triangle_area(
      [
        $points[$tris[$i][0]],
        $points[$tris[$i][1]],
        $points[$tris[$i][2]]
      ]
    );
  }
  echo "Fläche: ".$area." cm².";

Und hier der Code in Aktion. Es sei allerdings darauf hingewiesen, dass die Performance der Funktion mit zunehmender Zahl von Eckpunkten spürbar nachlässt. Mit anderen Worten, je mehr Eckpunkte ein Pfad enthält, umso länger dauert die Flächenberechnung.

Beliebige Flächen bestimmen #

Eine schnellere Methode zur Bestimmung der Fläche eines beliebigen Polygons bietet folgende Funktion:

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  function coord_path_area($points) {
    $area = 0;
    $c = count($points);
    for ($i = 0; $i < $c; $i++) {
      $x1 = $points[$i][0];
      $y1 = $points[$i][1];
      $x2 = $points[coord_increase($i, $c)][0];
      $y2 = $points[coord_increase($i, $c)][1];
      $area += ($x1 - $x2) * ($y1 + $y2);
    }
    return ($area / 2);
  }

  $points = [
    [1,4],
    [5,2],
    [12,3],
    [6,13],
    [12,12],
    [10,10],
    [18,7],
    [18,14],
    [14,18],
    [3,16],
  ];

  $area = coord_path_area($points);
  echo "Fläche: ".$area." cm².";

Kurvenverlauf einer Ellipse #

Eine Ellipse kann durch die Länge ihrer Halbachsen a und b beschrieben werden. Für die Punkte der Ellipse gilt, dass die Summe der Strecken zwischen einem beliebigen Punkt und den beiden Brennpunkten der Ellipse immer der doppelten Länge der großen Halbachse a entspricht. Außerdem gilt, dass jede Strecke, die aus den Teilstrecken a (große Halbachse) und b (kleine Halbachse) besteht und die die beiden Achsen miteinander verbindet, die jeweils durch die gegenüberliegenden Scheitelpunkte der Ellipse verlaufen, die Ellipse genau an dem Punkt schneidet, an dem sich die Teilstrecken a und b berühren, wie folgende Animation veranschaulicht:

a b

Es besteht also eine Relation zwischen dem Winkel α dieser Strecke zur Hauptachse der Ellipse und der Position des Schnittpunktes dieser Strecke mit der Ellipse. Die Position dieses Punktes relativ zum Mittelpunkt der Ellipse M lässt sich über folgende Gleichungen berechnen:

Formel 4 Formel 5

Wird für eine Umlaufbahn der Winkel der Strecke zur Hauptachse proportional zur Zeit ermittelt

Formel 6

wobei ein vollständiger Umlauf tU 360° also 2π entspricht, so ergibt sich aber keine Bahn mit konstanter Geschwindigkeit, da die regelmäßige Kreisbewegung der Strecke auf die Ellipse als gestauchten Kreis projiziert wird und der Verlauf der Geschwindigkeit so ebenfalls ›gestaucht‹ wird. Um die Hauptscheitelpunkte ist die Geschwindigkeit daher geringer als im Bereich um die Nebenscheitelpunkte.

Entsprechend der folgenden Darstellung werden für die korrekte Berechnung der Position des Punktes P folgende Werte benötigt:

α und r oder x oder y oder
β und s oder x oder y oder
x und y

a b c r s α β P x y t S

Die Länge der Strecke r lässt sich mit folgender Formel bestimmen (s. Polarkoordinaten bzgl. Brennpunkt):

Formel 7 oder in PHP:

$r = pow($b, 2) / ($a * (1 - $e * cos($alpha)));

Die Variable e enthält hier die numerische Exzentrizität der Ellipse, für die gilt:

Formel 8

Daraus folgt: Formel 9

Die Länge der Strecke s lässt sich mit folgender Formel bestimmen (s. Polarkoordinaten bzgl. Mittelpunkt):

Formel 10 oder in PHP:

$s = $b / sqrt(1 - pow($e, 2) * pow(cos($beta), 2));

Position eines Objekts auf einer Keplerbahn berechnen #

Ein Objekt, das sich auf einer sogenannten Keplerbahn um dessen Schwerpunkt bewegt, der sich in einem der Brennpunkte der elliptischen Bahn befindet, hat keine konstante Geschwindigkeit. Aufgrund der Gravitationskraft ist die Geschwindigkeit an der Apside in der Nähe des Zentralkörpers größer als an der entfernten Apside:

Die Polarkoordinaten des Objekts können mit Hilfe der Kepler-Gleichung bestimmt werden. Dazu wird die wahre Anomalie des Objekts errechnet. Das ist der Winkel zwischen der Periapsis und dem Objekt zu einem bestimmten Zeitpunkt mit dem Schwerpunkt als Scheitelpunkt. Die folgende Funktion anomaly() benötigt die numerische Exzentrizität der Ellipse sowie die Zeit, die seit der Passage des Objekts durch die Periapsis vergangen ist, als Argumente. Sie gibt die mittlere Anomalie $M, die exzentrische Anomalie $E sowie die wahre Anomalie $T zurück. $E wird hier mit Hilfe des Newton-Verfahrens in sechs Durchgängen ermittelt. Je höher die Anzahl der Durchgänge, umso größer die Genauigkeit.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  function anomaly($e, $t) {
    $M = 2 * pi() * $t;
    $E = $M;
    for ($i = 0; $i < 6; $i++) {
      $E = $E - ($E - $e * sin($E) - $M) / (1 - $e * cos($E));
    }
    $T = acos((cos($E) - $e) / (1 - $e * cos($E))) * (1 - 2 * (sin($E) < 0));
    return [$M, $E, $T];
  }
  
  $a = 149597870.7;  // große Halbachse in km
  $ex = 0.0167;  // numerische Exzentrizität der Umlaufbahn
  $P = 365.256;  // Dauer der Bahnperiode (Umlaufzeit) in Tagen
  $f = 150.23 / $P;  // Quotient aus dem Zeitpunkt der Messung in Tagen und der Bahnperiode
  $anom = anomaly($ex, $f);
  $alpha = $anom[2];  // wahre Anomalie
  $r = $a - $a * $ex * cos($anom[1]);  // Abstand vom Brennpunkt zum Objekt
  echo rad2deg($alpha);

Äquatoriale und horizontale Koordinaten berechnen #

Mit den folgenden Funktionen lassen sich aus geozentrisch-ekliptikalen Koordinaten eines Himmelskörpers die entsprechenden äquatorialen bzw. horizontalen Koordinaten ermitteln. Wobei gilt:

$lat = ekliptikale Breite
$lon = ekliptikale Länge
$obliq = Schiefe der Ekliptik
$lat2 = geografische Breite des Beobachters
$lmst = Sternzeit als Winkel

(Diese Funktionen wurden bei der Planetenkarte eingesetzt.)

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  function equatorial($lat, $lon, $obliq) {
    # returns equatorial from geocentric ecliptic coordinates
    $dec = asin( cos($obliq) * sin($lat) + sin($obliq) * cos($lat) * sin($lon));
    $ra  = atan2(cos($obliq) * sin($lon) - sin($obliq) * tan($lat),  cos($lon));
    $ra += 2 * pi() * ($ra < 0);
    return [$dec, $ra];
  }

  function horizontal($lat, $lon, $lat2, $lmst) {
    # returns horizontal from geocentric ecliptic coordinates
    $coords = equatorial($lat, $lon);
    $dec = $coords[0];
    $ra  = $coords[1];
    $alt = asin(sin($lat2) * sin($dec) + cos($lat2) * cos($dec) * cos($lmst - $ra));
    $azm = atan2(sin($ra - $lmst), cos($lat2) * tan($dec) - sin($lat2) * cos($lmst - $ra));
    $azm += 2 * pi() * ($azm < 0);
    return [$alt, $azm];
  }

Winkel umrechnen #

Mit den folgenden Funktionen können Winkel umgerechnet werden:

rad2dms() - Radiant nach Grad, Minute, Sekunde
rad2hms() - Radiant nach Stunde, Minute, Sekunde
dms2rad() - Umkehrfunktion von rad2dms()
hms2rad() - Umkehrfunktion von rad2hms()

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  function rad2dms($rad) {
    $sgn = 1 - (2 * ($rad < 0));
    $rad = abs($rad);
    $t = rad2deg($rad);
    $d = floor($t);
    $m = floor(($t - $d) * 60);
    $s = (($t - $d) * 60 - $m) * 60;
    return [$sgn * $d, $m, $s];
  }
  
  function rad2hms($rad) {
    $sgn = 1 - (2 * ($rad < 0));
    $rad = abs($rad);
    $t = 12 * $rad / pi();
    $h = floor($t);
    $m = floor(($t - $h) * 60);
    $s = (($t - $h) * 60 - $m) * 60;
    return [$sgn * $h, $m, $s];
  }
  
  function dms2rad($d, $m, $s) {
    $sgn = 1 - (2 * substr_count($d, "-"));
    return $sgn * deg2rad((abs($d) * 3600 + $m * 60 + $s) / 3600);
  }
  
  function hms2rad($h, $m, $s) {
    $sgn = 1 - (2 * substr_count($h, "-"));
    return $sgn * deg2rad((abs($h) * 3600 + $m * 60 + $s) / 240);
  }

Achteck in einem Quadrat #

Wie lang sind die Seiten eines gleichmäßigen Achtecks, das mit vier Seiten auf einem Quadrat aufliegt?

Bekannt:
a = Seitenlänge des Quadrats

Gesucht:
x = Seitenlänge des Achtecks

a x

Formel:
Formel 11 oder in PHP:

Quelltext auswählen
1
$x = $a - 2 * ($a - sqrt(2 * pow($a / 2, 2)));

Höhe über einer Seite eines beliebigen Dreiecks #

Bekannt:
a = Seitenlänge des Dreiecks
β = Winkel an einem Ende der Seite a
γ = Winkel an dem anderen Ende der Seite a

Gesucht:
h = Höhe über der Seite a

a β γ h

Formel:
Formel 12 oder in PHP:

Quelltext auswählen
1
$h = $a / (cos($beta) / sin($beta) + cos($gamma) / sin($gamma));

Länge der dritten Seite eines beliebigen Dreiecks #

Bekannt:
a = erste Seitenlänge des Dreiecks
b = zweite Seitenlänge des Dreiecks
γ = Winkel zwischen den gegebenen Seiten

Gesucht:
c = Länge der dritten Seite

a b c γ

Formel:
Formel 13 oder in PHP:

Quelltext auswählen
1
$c = sqrt(pow(sin($gamma) * $a, 2) + pow($b - sqrt(pow($a ,2) - pow(sin($gamma) * $a, 2)), 2));

Variante:

Bekannt:
a = erste Seitenlänge des Dreiecks
b = zweite Seitenlänge des Dreiecks
α = Winkel zwischen einer der gegebenen Seiten und c

Gesucht:
c = Länge der dritten Seite

a b c α

Formel:
Formel 14 oder in PHP:

Quelltext auswählen
1
$c = sqrt(pow($a, 2) - pow(sin($alpha) * $b, 2)) + sqrt(pow($b, 2) - pow(sin($alpha) * $b, 2));

Länge der zweiten Seite eines beliebigen Dreiecks #

Bekannt:
a = erste Seitenlänge des Dreiecks
α = Winkel an einem Ende der Seite a
β = Winkel an dem anderen Ende der Seite a

Gesucht:
c = zweite Seitenlänge des Dreiecks

a c α β

Formel:
Formel 15 oder in PHP:

Quelltext auswählen
1
$c = $a * (cos($alpha) + sin($alpha) * cos(pi() - $alpha - $beta) / sin(pi() - $alpha - $beta));

Winkel in einem beliebigen Dreieck #

Bekannt:
a = erste Seitenlänge des Dreiecks
b = zweite Seitenlänge des Dreiecks
c = dritte Seitenlänge des Dreiecks

Gesucht:
α = einer der Winkel

a b c α

Formel:
Formel 16 oder in PHP:

Quelltext auswählen
1
$alpha = acos((pow($a, 2) - pow($b, 2) - pow($c, 2)) / (-2 * $b * $c));