Approximation von Funktionen durch Bezierkurven

Zur Navigation springen Zur Suche springen

SVG kann keine Funktionen darstellen, sondern ist auf Gerade, Kreise, Ellipsen und Bezierkurven beschränkt.

Bei einer Darstellung eines Funktionsgraphen in SVG muss man sich zunächst überlegen, wie er sich am besten durch Bezierkuren approximieren lässt. In einigen Fällen funktioniert diese Annäherung mit überraschend wenigen Parametern bereits sehr gut.

Links

Mathematik der Approximation

Gegegeben seien vier Punkte einer (genügend glatten, sprich mindestens 2x stetig differenzierbaren) Funktion, und mit . Dann errechnen sich die Stützpunkte einer (von vielen möglichen) Bezierkurve folendermaßen:

Setzt man , so kann man leicht nachrechnen, dass und ist.

Kubisches Polynom

Das einfachste kubische Polynom

ergibt folgende Stützpunkte:

Berechnung

=

Der besseren Veranschaulichung halber habe ich eine zweite Kurve eingezeichnet (blau), deren X-Achse auf das 10-fache gestreckt ist.

Kubisches Polynom mit drei Nullstellen

Das kubische Polynom (der Einfachheit halber symmetrisch zum Ursprung)

lässt sich exakt als Kombination einer Bezierkurve und ihrer Spiegelung am Ursprung darstellen.

Sinusfunktion

Ich habe die Sinusfunktion im 1. Quadranten approximiert. Zum Vergleich habe ich eine Sinuskurve durch 45 äquidistante Stützstellen und linerare Interpolation erzeugt, in der Grafik in grau.

Die ursprünglich berechnete Bezierkurve (cyan) hat im Endpunkt eine Tangente, die nicht exakt horizontal ist. Daher habe ich eine zweite Bezierkurve (dunkelblau) erzeugt, indem ich die Y-Koordinate des 2. Stützpunktes durch die Y-Koordinate des Endpunktes ersetzt habe.

Verblüffend ist die Übereinstimmung der Bezierkurve mit der Sinuskurve, obwohl nur 4 Punkte approximiert wurden.
Winkel Wert Näherung
0 0 0 0 0
30 0.5 209 18*209-9*362+2*418=1340 -9*209+18*362-5*418=2545
60 362 233.33 848.33
90 1 418
45 0.7071 577 18*577-9*816+2*577=4196 -9*577+18*816-5*577=6610
90 1 816 699.33 1001.67
135 0.7071 577

SVG Code

<?xml version="1.0" encoding="UTF-8"?>
<svg
 version="1.1"
 viewBox="-10 -500 920 610"
 height="610" width="920"
 xmlns:xlink="http://www.w3.org/1999/xlink"
 xmlns="http://www.w3.org/2000/svg">
 <title>Sinuskurve approximiert durch eine einzelne Bezierkurve</title>
 <defs>
  <pattern id="_10px" width="10" height="10" patternUnits="userSpaceOnUse">
   <path d="M0,0 H10 M0,10 V0" opacity=".6" stroke="lime" stroke-width=".5"/>
  </pattern>
  <pattern id="_50px" width="50" height="50" patternUnits="userSpaceOnUse">
   <path d="M0,0 H50 M0,50 V0" stroke="#0a0"/>
  </pattern>
  <pattern id="_100px" width="100" height="100" patternUnits="userSpaceOnUse">
   <path d="M0,0 H100 M0,100 V0" stroke="#080" stroke-width="1.5"/>
  </pattern>
  <rect id="paper" x="-10" y="-1100" width="400%" height="400%"/>
  <path id="sinuskurve" d="m0 0 2 3.4899 2 3.4857 2 3.4772 2 3.4645 2 3.4475
  2 3.4264 2 3.401 2 3.3715 2 3.338 2 3.3003 2 3.2586 2 3.213
  2 3.1635 2 3.11 2 3.0528 2 2.9919 2 2.9274 2 2.8592 2 2.7876
  2 2.7126 2 2.6343 2 2.5528 2 2.4681 2 2.3805 2 2.29 2 2.1966
  2 2.1006 2 2.0021 2 1.9011 2 1.7977 2 1.6922 2 1.5846 2 1.4751
  2 1.3638 2 1.2509 2 1.1364 2 1.0205 2 0.9034 2 0.78519 2 0.66602
  2 0.54603 2 0.42538 2 0.30422 2 0.18268 2 0.060917"/>
  <path id="bezierapprox" d="m0 0c300 233.33 600 424.17 900 418"/>
  <path id="bezierapprox1" d="m0 0c300 233.33 600 418 900 418"/>
  <path id="con" d="m0 0 300 233.33m600 184.67-300 6"/>
  <path id="con1" d="m0 0 300 233.33m600 184.67h-300"/>
 </defs>
 <g transform="scale(.5 .418)" opacity=".5">
  <use fill="url(#_10px)" xlink:href="#paper"/>
  <use fill="url(#_50px)" xlink:href="#paper"/>
  <use fill="url(#_100px)" xlink:href="#paper"/>
 </g>
 <g transform="scale(1,-1)">
  <g fill="cyan" stroke="cyan">
   <use fill="none" xlink:href="#bezierapprox"/>
   <circle r="7"/>
   <circle cx="300" cy="233" r="7"/>
   <circle cx="600" cy="424" r="7"/>
   <circle cx="900" cy="418" r="7"/>
   <use fill="none" xlink:href="#con"/>
  </g>
  <g fill="grey" stroke="grey">
   <circle cx="300" cy="209" r="5"/>
   <circle cx="600" cy="362" r="5"/>
   <use transform="scale(10,4.18)" fill="none" stroke-width=".3" xlink:href="#sinuskurve"/>
  </g>
  <g fill="blue" stroke="blue">
   <use fill="none" xlink:href="#bezierapprox1"/>
   <use fill="none" xlink:href="#con1"/>
   <circle cx="600" cy="418" r="7"/>
  </g>
 </g>
 <g font-family="Liberation Serif">
  <text font-size="20" text-anchor="middle">
   <tspan x="0" y="20">0</tspan>
   <tspan x="300" y="-180">30</tspan>
   <tspan x="600" y="-340">60</tspan>
   <tspan x="900" y="-400">90</tspan>
  </text>
 </g>
</svg>

Sinus eine volle Periode

Pfad

<path id="sinus" transform="scale(.5,-1.19617)"
  d="m0 0
     c300 233.33 600 418 900 418 s600 -185 900 -418
     s600 -418 900 -418 s600 185 900 418
     s600 418 900 418"/>

Arcus Sinus

<path id="sinus" transform="scale(.46444,-1)"
  d="m0 0
     c-300 -233.33 -600 -418 -900 -418
     M0,0 c300 233.33 600 418 900 418
     "/>
  <path id="arcsinus" transform="scale(-1,-.46444)"
  d="m418 -900
     c0,300 -185.33,600 -418,900
     s-418,600 -418,900
     "/>