SVG/Anwendung und Praxis/Funktionsplotter

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche


Es wird ein Script vorgestellt, das mathematische Funktionen über Inputelemente einliest und grafisch darstellt. Für die grafische Darstellung wird das SVG-Element verwendet.

Es gibt auch eine Version des Funktionsplotters, die das Canvas-Element verwendet.

Das Script besteht aus dem Grafikobjekt grafik, dem Plotobjekt plot und dem Funktionsplotter.


Das Grafikobjekt[Bearbeiten]

Das Grafikobjekt ist die Schnittstelle zur SVG.

SW.grafik(grafikelement)
legt das SCG-Element an.
grafikelement: ID des Elements oder Referenz auf das Element, in dem das SVG-Element angelegt wird.

SW.grafik definiert die Methoden:

.setwidth(width)
setzt die Linienstärke.
width: Linienstärke in Pixeln.
.line(xs,ys,xe,ye,color)
zeichnet eine Linie von (xs,ys) nach (xe,ye) in Farbe color.
xs,ys: Koordinaten des Startpunkts in Pixeln,
xe,ye: Koordinaten des Endpunkts in Pixeln,
color: Linienfarbe im css-Format.
.polyline(points,color)
zeichnet einen Linienzug.
points: Array der Form [{x: xwert, y: ywert},{…},…] mit den Koordinaten der Stützpunkte in Pixeln.
color: Linienfarbe im css-Format.
.polyfill(points,color,a)
zeichnet einen Linienzug. Die vom Linienzug umschlossene Fläche wird eingefärbt.
points: Array der Form [{x: xwert, y: ywert},{…},…] mit den Koordinaten der Stützpunkte in Pixeln.
color: Füllfarbe im css-Format.
alpha: Transparenz, 0<=alpha<=1
.text(x,y,size,color,text,align,direction)
gibt Text aus.
x,y: Koordinaten des Bezugspunkts in Pixeln,
size: Texthöhe im css-Format
color: Linienfarbe im css-Format.
align: String aus zwei Buchstaben, gibt die Lage des Bezugspunkts relativ zum Text an. Der erste Buchstabe darf die Werte l, m, oder r annehmen für links, mitte oder rechts. Der zweite Buchstabe darf die Werte o, m oder u annehmen für oben, mitte oder unten.
direction: gibt die Laufrichtung des Textes an, darf die Werte h und v für horizontal und vertikal annehmen.
.del()
löscht die Grafik.
Beispiel: Das Grafikobjekt
"use strict";

var SW = window.SW || {};

// Das Grafikobjekt
SW.grafik = function(grafikelement) {
  this.method = "svg";
  // SVG in Größe des "grafikelement" anlegen
  if(typeof grafikelement == "string") grafikelement = document.getElementById(grafikelement);
  this.w = grafikelement.offsetWidth;
  this.h = grafikelement.offsetHeight;
  var linewidth = 1;
  var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svg.setAttribute("width","100%");
  svg.setAttribute("height","100%");
  svg.setAttribute("viewBox","0 0 "+this.w+" "+this.h);
  svg.setAttribute("preserveAspectRatio","none");
  grafikelement.appendChild(svg);
  
  // Linienstärke setzen
  this.setwidth = function(width) {
    linewidth = width;
  } // setwidth
  
  // Linie von (xs,ys) nach (xe,ye) in Farbe color zeichnen
  this.line = function(xs,ys,xe,ye,color) {
    var linie = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    linie.setAttribute("x1",xs);
    linie.setAttribute("y1",this.h-ys);
    linie.setAttribute("x2",xe);
    linie.setAttribute("y2",this.h-ye);
    linie.setAttribute('stroke', color);
    linie.setAttribute('stroke-width', linewidth);
    linie.setAttribute("vector-effect","non-scaling-stroke");
    svg.appendChild(linie);
  } // line

  // Polylinie mit den Werten in points in Farbe color zeichnen
  this.polyline = function(points,color) {
    var polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
    polyline.setAttribute('stroke', color);
    polyline.setAttribute('stroke-width', linewidth);
    polyline.setAttribute('fill', "none");
    polyline.setAttribute("vector-effect","non-scaling-stroke");
    var pointstring = "";
    for(var i=0;i<points.length;i++) pointstring += points[i].x+","+(this.h-points[i].y)+" ";
    polyline.setAttribute('points', pointstring);
    svg.appendChild(polyline);
  } // polyline

  // Polylinie mit den Werten in points zeichnen
  // Die von der Polylinie umschlossene Fläche wird in Farbe color mit Alphawert alpha eingefärbt
  this.polyfill = function(points,color,alpha) { 
    var polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
    polygon.setAttribute('stroke', "none");
    polygon.setAttribute('fill', color);
    polygon.setAttribute('fill-opacity', alpha);
    var pointstring = "";
    for(var i=0;i<points.length;i++) pointstring += points[i].x+","+(this.h-points[i].y)+" ";
    polygon.setAttribute('points', pointstring);
    svg.appendChild(polygon);
  } // polyfill
  
  // Text an (x,y) ausgeben
  // size: Schriftgröße
  // text: Text
  // align: Bezug für (x,y), zwei Buchstaben, z.B. lu für links unten, s. case
  // diretion: Textrichtung: v für vertikal, sonst horizontal
  this.text = function(x,y,size,color,text,align,direction) {
    var stext = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    stext.style.fontSize = size;
    stext.style.color = color;
    stext.style.fill = color;
    stext.textContent = text;
  
    var align_h = "m";
    var align_v = "m";
    if(align && align.length) {
      align_h = align.substr(0,1);
      if(align.length>1) align_v = align.substr(1,1);
    }
    switch(align_h) {
      case "l": stext.setAttribute("text-anchor","start"); break;
      case "m": stext.setAttribute("text-anchor","middle"); break;
      case "r": stext.setAttribute("text-anchor","end"); break;
      default:  stext.setAttribute("text-anchor","middle"); break;
    }
    switch(align_v) {
      case "o": stext.setAttribute("dy","1.1em"); break;
      case "m": stext.setAttribute("dy","0.3em"); break;
      case "u": stext.setAttribute("dy","-0.1em"); break;
      default:  stext.setAttribute("dy","-0.3em"); break;
    }
    
    stext.setAttribute("x",x);
    stext.setAttribute("y",this.h-y);
    if(direction && direction=="v") stext.setAttribute("transform","rotate(270 "+x+" "+(this.h-y)+")");
    
    svg.appendChild(stext);
    
  // Werte für unscale
    stext.setAttribute("class","noscale");
    stext.xorg = x;
    stext.yorg = this.h-y;
    if(direction && direction=="v") stext.phiorg = 270;
    else stext.phiorg = 0;
  } // text

  // Canvas löschen
  this.del = function() {
    grafikelement.innerHTML = "";
  } // del
  
  // Einige automatische Skalierungen rückgängig machen
  var unscale = function() {
    var etext = document.querySelectorAll(".noscale"),esvg,m;
    for(var i=0;i<etext.length;i++) {
      esvg = etext[i].ownerSVGElement;
      m = esvg.getScreenCTM();
      etext[i].setAttribute("transform","scale("+1/m.a+" "+1/m.d+")" + "rotate("+etext[i].phiorg+" "+etext[i].xorg*m.a+" "+etext[i].yorg*m.d+")");
      etext[i].setAttribute("x",etext[i].xorg*m.a);
      etext[i].setAttribute("y",etext[i].yorg*m.d);
    }
  }
  window.addEventListener("resize",unscale);
} // grafik

SVG-Grafiken können mit dem SVG-Element mit skalieren. Dazu wurde den SVG-Element eine viewbox gegeben und der Parameter .setAttribute("preserveAspectRatio","none") gesetzt. Leider skalieren auch die Linienstärke und die Textgröße mit. Das Skalieren der Linienstärke wurde mit .setAttribute("vector-effect","non-scaling-stroke") unterbunden. Um das Skalieren des Textes ebenfalls zu unterbinden, wird Javascript benötigt. Es wird ein Eventhandler für das resize-Event notiert, in dem die neue Skalierung des SVG-Elements abgefragt und dann bei den Text-Elementen rückgängig gemacht wird.

Das folgende Beispiel zeigt die Anwendung des Grafikobjekts.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Beispiel: Test Grafikobjekt auf SVG-Basis</title>
<style>
  #plotarea { background-color:#ffffff; width:90%; height:80vh; border: 1px solid black }
</style>
<script src="grafik_svg.js"></script>
<script>
"use strict";
window.addEventListener("DOMContentLoaded",function() {
  var grafik = new SW.grafik("plotarea");
  grafik.line(20,20,grafik.w-20,40,"black");
  var werte = [{x:20 ,y:25 },{x:15 ,y:grafik.h-10 },{x:grafik.w-10 ,y:grafik.h-50 },{x:grafik.w-30 ,y:100 },{x:grafik.w-100 ,y:50 }];
  grafik.setwidth(3);
  grafik.polyline(werte,"green");
  werte = [{x:70 ,y:70 },{x:70 ,y:grafik.h-70 },{x:grafik.w-70 ,y:grafik.h-70 },{x:grafik.w-70 ,y:70 }];
  grafik.polyfill(werte,"red",0.2);
  grafik.text(70,70,"0.8em","blue","Text_ro","ro","h"); // Referenzpunkt rechts oben
  grafik.text(70,70,"1em","blue","Text_lu","lu","h"); // Referenzpunkt links unten
  grafik.text(grafik.w-70,grafik.h/2,"1.5em","blue","Text_mm","mm","v"); // Referenzpunkt mitte mitte
});
</script>
</head>

<body>
  <h1>Beispiel: Test Grafikobjekt auf SVG-Basis</h1>
  <figure id="plotarea"></figure>
</body>
</html>

Das Plotobjekt[Bearbeiten]

Das Plotobjekt erstellt das Diagramm mit Achsen und Achsenbeschriftung.

SW.plot(feld,xstr,ystr)
legt den Bereich für das Diagramm an.
Feld: ID des Elements oder Referenz auf das Element, in dem das Diagramm angelegt wird.
xstr, ystr: Bezeichner der Objektelemente mit den x- und y-Werten im Datenarray. Defaultwerte sind x und y. Das Datenarray sieht dann so aus: [{x: xwert, y: ywert}{...},...]

SW.plot definiert die Methoden:

.scale(daten)
ermittelt zu den Werten im Array daten die kleinsten und größten Werte.
a: Array der Form [{xstr: xwert, ystr: ywert}{...},...]
.clear()
löscht das Diagramm
.frame(x0,y0,xtext,ytext)
legt das Achsenkreuz an.
x0, y0: Koordinaten der linken unteren Ecke in Pixel.
xtext, ytext: Beschriftung der Achsen.
.plot(daten,color) Plottet die Daten.
daten: Array der Form [{xstr: xwert, ystr: ywert}{...},...]
color: Linienfarbe im css-Format.

Weitere Eigenschaften und deren Defaultwerte:

.ticwidth 
Linienstärke der Gitterlinien (1)
.linewidth 
Linienstärke der Plotlinien (1)
.borderwidth 
Linienstärke des Rahmens um den Plot (2)
.framecol 
Farbe des Rahmens ("black")
.gridcol 
Farbe der Gitterlinien ("gray")
.labelcol 
Farbe der Achsenbeschriftung ("black")
.fillopac 
Deckkraft der Füllfarbe unter der Plotlinie (0.1)
Beispiel: Das Plotobjekt
"use strict";

var SW = window.SW || {};

// Math.log10 wird noch nicht von allen Browsern unterstützt
if(!Math.log10) Math.log10 = function(x) { return Math.log(x)/Math.LN10; };

// Das Plotobjekt
// feld ist das Objekt bzw. dessen Id, in dem das Diagramm erstellt werden soll
// xstr und ystr geben die Bezeichner der Objektelemente mit den x- und y-Werten im Datenarray an.
// Defaultwerte sind x und y. Das Datenarray sieht dan so aus: [{x:xwert,y:ywert}{...},...]
SW.plot = function(feld,xstr,ystr) {
  // Defaultwerte
  this.ticwidth = 1;
  this.linewidth = 1;
  this.borderwidth = 2;
  this.framecol = "black";
  this.gridcol = "gray";
  this.labelcol = "black";
  this.fillopac = 0.1;
  
  // Plotbereich anlegen
  if(typeof feld == "string") feld = document.getElementById(feld);
  feld.innerHTML = "";

  // Einige Variablen
  var xobj = xstr?xstr:"x";
  var yobj = ystr?ystr:"y";
  var xmin=0,xmax=0,ymin=0,ymax=0;
  var xfak=0,yfak=0;
  var dx,dy,fx,fy;
  var gr = null;

  // Zu den Werten in daten xmin, xmax, ymin und ymax ermiteln
  this.scale = function(daten) {
    if(xmin==xmax) { // Startwerte beim ersten Datensatz
      xmax = xmin = daten[0][xobj];
      ymax = ymin = daten[0][yobj];
    }
    for(var i=1;i<daten.length;i++) {
      var t = daten[i];
      if(t[xobj]<xmin) xmin = t[xobj];
      if(t[xobj]>xmax) xmax = t[xobj];
      if(t[yobj]<ymin) ymin = t[yobj];
      if(t[yobj]>ymax) ymax = t[yobj];
    }
  } // scale
  
  // Plotbereich leeren
  this.clear = function() {
    feld.innerHTML = "";
    xmax = xmin = ymax = ymin = xfak = yfak = 0;
  } // clear

  // Achsenkreuz, Tics und Beschriftung, linke untere Ecke bei (x0,y0)
  // xtext und ytext sind die Beschriftungen der Achsen
  this.frame = function(x0,y0,xtext,ytext) {
    this.x0 = x0;
    this.y0 = y0;
    // Den Bereich für das Diagramm anlegen
    feld.innerHTML = "";
    gr = new SW.grafik(feld);
    this.method = gr.method;
    // Achsenbeschriftungen
    if(xtext.length) gr.text((gr.w-x0)/2+x0,0,".9em",this.labelcol,xtext,"mu","h"); 
    if(ytext.length) gr.text(10,(gr.h-y0)/2+y0,".9em",this.labelcol,ytext,"mm","v");
    // xmin und xmax auf die nächst kleinere bzw. größere "glatte" Zahl runden und den 
    // Abstand der Tics auf glatte Zahlen (1 2 5 0) für x-Achse legen
    if(xmax==xmin) { xmin -= 0.5; xmax += 0.5; }
    dx = (xmax - xmin)/100;    
    xmin -= dx; xmax += dx;
    dx = xmax - xmin;
    fx = Math.pow(10,Math.floor(Math.log10(dx))-1); // Die Größenordnung ermitteln
    xmin = Math.floor(xmin/fx)*fx;
    xmax = Math.ceil(xmax/fx)*fx;
    xfak = (gr.w-x0)/(xmax-xmin);
    var tx = ticdist(100*dx/gr.w);
    var mxmin = Math.ceil(xmin/tx)*tx;
    // Tics und Zahlen an der x-Achse
    gr.setwidth(this.ticwidth);
    for(var x=mxmin;x<=xmax;x+=tx) {
      var xx = (x-xmin)*xfak + x0;
      gr.line(xx,y0,xx,gr.h,this.gridcol);
      if(xtext.length && xx<(gr.w-5) && xx>5) gr.text(xx,y0-2,".8em",this.labelcol,myround(x,tx),"mo","h");
    }
    // ymin und ymax auf die nächst kleinere bzw. größere "glatte" Zahl runden und den 
    // Abstand der Tics auf glatte Zahlen (1 2 5 0) für x-Achse legen
    if(ymax==ymin) { ymin -= 0.5; ymax += 0.5; }
    dy = (ymax - ymin)/100; 
    ymin -= dy; ymax += dy;
    dy = ymax - ymin;
    fy = Math.pow(10,Math.floor(Math.log10(dy))-1); // Die Größenordnung ermitteln
    ymin = Math.floor(ymin/fy)*fy;
    ymax = Math.ceil(ymax/fy)*fy;
    yfak = (gr.h-y0)/(ymax-ymin);
    var ty = ticdist(gr.h<250 ?  50*dy/gr.h : 100*dy/gr.h);
    var mymin = Math.ceil(ymin/ty)*ty;
    // Tics und Zahlen an der y-Achse
    for(var y=mymin;y<=ymax;y+=ty) {
      var yy = (y-ymin)*yfak + y0;
      gr.line(x0,yy,gr.w,yy,this.gridcol);
      if(ytext.length && yy<(gr.h-5) && yy>5) gr.text(x0-2,yy,".8em",this.labelcol,myround(y,ty),"rm","h");
    }
    gr.setwidth(this.borderwidth);
    gr.polyline([
      {x:x0, y: y0},
      {x:gr.w-this.borderwidth, y:y0},
      {x:gr.w-this.borderwidth, y:gr.h-this.borderwidth},
      {x:x0, y:gr.h-this.borderwidth},
      {x:x0, y:y0}],
      this.framecol);
  } // frame

  // Daten Plotten
  // daten: Datenarray mit Objekten mit den x- und y-Werten
  // color Diagrammfarbe
  this.plot = function(daten,color) {
    var arr=[];
    for(var i=0,l=daten.length;i<l;i++)
      arr.push({x:(daten[i][xobj]-xmin)*xfak+this.x0, y:(daten[i][yobj]-ymin)*yfak+this.y0});
    if(this.fillopac>0) {
      var fillline;
      if(ymax*ymin<=0) fillline = -ymin*yfak+this.y0 ; 
      else if(ymin>0) fillline = 1+this.y0;
      else fillline = gr.h-1;
      arr.push({x:(daten[l-1][xobj]-xmin)*xfak+this.x0,y:fillline});
      arr.push({x:(daten[0][xobj]-xmin)*xfak+this.x0,y:fillline});
      arr.push({x:(daten[0][xobj]-xmin)*xfak+this.x0,y:(daten[0][yobj]-ymin)*yfak+this.y0});
      gr.polyfill(arr,color,this.fillopac);
      arr.length -= 3;
    }
    gr.setwidth(this.linewidth);
    gr.polyline(arr,color);
  } // plot
  
  // Hilfsfunktionen zum Runden
  var myround = function(z,d) { 
    var l10 = Math.floor(Math.log10(d));
    var f = Math.pow(10,l10); 
    var zz = Math.round(z/f)*f;
    var zzz = Number(zz.toPrecision(15)).toString(10);
    return zzz; 
  }
  
  // Hilfsfunktion zum berechnen des Abstands der Achsen-Tics, Abstände auf 1 2 5 0 gerundet
  var ticdist = function(td) { 
    var td10 = Math.pow(10,Math.floor(Math.log10(td)));
    td = Math.round(td/td10);
    td = Number(String(td).replace(/3/,"2").replace(/[4567]/,"5").replace(/[89]/,"10"));
    td *= td10;
    return td;
  } // ticdist
} // plot

Das folgende Beispiel zeigt die Anwendung des Plotobjekts:

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Beispiel: Test Plotobjekt auf SVG-Basis</title>
<style>
  #plotarea { background-color:#ffffff; margin:0; height:calc(100vh - 7em); min-height:300px; }
</style>
<script src="grafik_svg.js"></script>
<script src="plot.js"></script>
<script>

"use strict";

window.addEventListener("DOMContentLoaded",function() {
  var daten = [],
    xmin = -Math.PI,
    xmax = Math.PI,
    npt = 1000;
  var dx = ( xmax - xmin ) / npt;    
  for(var x=xmin;x<=xmax;x+=dx) daten.push( { x: x, y: Math.sin(x) } );
  var plt = new SW.plot("plotarea") ;
  plt.scale(daten);
  plt.frame(50,35,"x","sin(x)");
  plt.plot(daten,"red");
});
</script>
</head>
<body>
  <h1>Beispiel: Test Plotobjekt auf SVG-Basis</h1>
  <figure id="plotarea"></figure>
</body>
</html>

Der Funktionsplotter[Bearbeiten]

Der Funktionsplotter befindet sich im Eventhandler für das Ereignis DOMContentLoaded. Hier werden die Referenzen auf die HTML-Elemente ermittelt und die benötigten Eventhandler angelegt.

In der Funktion fkt_plotter werden dann die Inputfelder mit den Funktionen und den Min- und Maxwerten für die X-Achse ausgelesen, die Funktionswerte berechnet und der Plot erstellt. Zusätzlich wird noch in einem versteckten Div-Element eine gekürzte Wertetabelle für Screenreader angelegt.

Das Plotterscript sieht so aus:

Beispiel: Das Plotterscript
"use strict";

window.addEventListener("DOMContentLoaded",function() {
  // Zahlen einlesen und bei Bedarf korrigieren
  var get_num = function(e) {
    var num = e.value;
    if (isNaN(num)) { num = num.replace(/,/g,"."); }
    if (isNaN(num) || num.length==0 ) { num = 0.0; e.value = num; }
    return parseFloat(num);
  }
  // Der Funktionsplotter
  var fkt_plotter = function() {
    var funktionen = [];
    // Funktionen lesen
    for(var i=0;i<e_fkt.length;i++ ) funktionen[i] = e_fkt[i].value;
    // xmin und xmax lesen, prüfen und bei Bedarf korrigieren
    var xmin = get_num(e_xmin);
    var xmax = get_num(e_xmax);
    if (xmax==xmin) {
      xmin -= 0.5; xmax += 0.5;
      e_xmin.value = xmin;
      e_xmax.value = xmax;
    }
    else if (xmax<xmin) {
      var t = xmax;
      xmax = xmin;
      xmin = t;
      e_xmin.value = xmin;
      e_xmax.value = xmax;
    }
    var dx = (xmax - xmin)/npt;
    // Werte für Diagramm berechnen ...
    var daten = [];
    for(var i=0;i<funktionen.length;i++) {
      try { 
        if(funktionen[i].length && funktionen[i].search(/\S/)!=-1) {
          var fun = new Function("x","with(Math){return("+funktionen[i]+")}");
          var ar = [];
          for(var x=xmin;x<=xmax;x+=dx) ar.push( { x: x, y: fun(x) } );
          daten.push(ar);
        }
      }
      catch(e) { 
        alert("Fehler in der Formel Nr "+(i+1)+":\n\n"+funktionen[i]+"\n\n"+e); 
      }
    }
    // ... und plotten
    plt.clear();
    plt.fillopac = .3;
    for(var i=0;i<daten.length;i++) if(!isNaN(daten[i][0].y)) plt.scale(daten[i]);
    plt.frame(50,35,"x","y");
    for(var i=0;i<daten.length;i++) if(!isNaN(daten[i][0].y)) plt.plot(daten[i],cols[i]);
    // ... und Ausgabe in versteckter Tabelle für Screenreader
    var tabelle = "";
    for(var j=0;j<daten.length;j++) {
      tabelle += "<table>";
      tabelle += "<caption>Wertetabelle für Funktion "+(j+1)+"</caption>";
      tabelle += "<thead><tr><th colspan='2'>"+funktionen[j]+"</th></tr>";
      tabelle += "<tr><th>x</th><th>y"+j+"</th></tr></thead>";
      tabelle += "<tbody>";
      for(var i=0;i<daten[j].length;i+=npt/20 ) tabelle += "<tr><td>"+daten[j][i].x.toFixed(3)+"</td><td>"+daten[j][i].y.toFixed(3)+"</td></tr>";
      tabelle += "</tbody>"
      tabelle += "</table>";
    }
    e_wertetabelle.innerHTML = tabelle;
  } // fkt_plotter

  var i,j,e_plotarea,e_wertetabelle,e_fkt=[],e_xmin,e_xmax,plt,e_jsfkt,e_vdeffkt;
  var npt = 1000;
  var current = 0;
  var cols = ["#ff0000","#008000","#0000ff"];
  var vdef_fkt = { 
    AM: "(1-0.3*sin(x/4))*sin(3*x)",
    PM: "sin(3*x-2*sin(x/3))",
    "sin(x)/x": "sin(x)/x",
    sinh: "(exp(x)-exp(-x))/2",
    cosh: "(exp(x)+exp(-x))/2",
    tanh: "(exp(x)-exp(-x))/(exp(x)+exp(-x))",
    Puls: "(function(){var y=0;for(var ii=10;ii<30;ii++) y+=sin(ii*x);return y})(x)"
  };
  var js_fkt = {
    abs: "abs(x)",
    acos: "acos(x)",
    asin: "asin(x)",
    atan: "atan(x)",
    cos: "cos(x)",
    exp: "exp(x)",
    log: "log(x)",
    pow: "pow(x,2)",
    sin: "sin(x)",
    sqrt: "sqrt(x)",
    tan: "tan(x)",
  };

  // Referenzen
  e_plotarea = document.getElementById("plotarea");
  e_wertetabelle = document.getElementById("wertetabelle");
  e_xmin = document.getElementById("idxmin");
  e_xmax = document.getElementById("idxmax");
  e_jsfkt = document.querySelectorAll("#jsfkt button"); 
  e_vdeffkt = document.querySelectorAll("#vdeffkt button"); 
  for(i=0;i<3;i++) {
    e_fkt[i] = document.getElementById("fkt"+(i+1));
    document.getElementById("f"+(i+1)).style.color = cols[i];
  }
  // Die Eventhandler
  document.getElementById("plotbutton").addEventListener("click",fkt_plotter);
  document.getElementById("fkt1").addEventListener("click",function(){ current = 0 });
  document.getElementById("fkt2").addEventListener("click",function(){ current = 1 });
  document.getElementById("fkt3").addEventListener("click",function(){ current = 2 });
  for(i=0;i<e_vdeffkt.length;i++) e_vdeffkt[i].addEventListener("click",function() { 
    e_fkt[current].value = vdef_fkt[this.innerHTML] });
  for(i=0;i<e_jsfkt.length;i++) e_jsfkt[i].addEventListener("click",function() { 
    e_fkt[current].value += "+"+ js_fkt[this.innerHTML] });
  // Plottbereich anlegen und Startwerte plotten
  plt = new SW.plot(e_plotarea);
  fkt_plotter();
  // SVG kann sich an Größenänderungen anpassen, bei Canvas muss der Plot neu erstellt werden
  if(plt.method=="canvas")
    window.addEventListener("resize",function() {
      plt = new SW.plot(e_plotarea);
      fkt_plotter();
    });
});


Das folgende Beispiel zeigt den Funktionsplotter.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Plottet Funktionen">
<title>Beispiel: Funktionsplot auf SVG-Basis</title>
<style>
  #plotarea { background-color:#ffffff; margin:0; height:calc(100vh - 19em); min-height:300px; }
  #plotter { margin-bottom: 1em; border: none }
  #plotter div { display:inline-block; vertical-align:top }
  #plotter div:nth-child(1) label { display:block; }
  #plotter div:nth-child(2) button { display:block; margin: 1em 0 0 5em;}	
  #idxmin, #idxmax { text-align:right; width:10em; }
  #fkt1, #fkt2, #fkt3 { width:30em; }
</style>
<script src="grafik_svg.js"></script>
<script src="plot.js"></script>
<script src="fkt_plotter.js"></script>
</head>

<body>
  <h1>Beispiel: Funktionsplot auf SVG-Basis</h1>
  <p>Stellt mathematische Funktionen grafisch dar.</p>
  <figure id="plotarea"></figure>
  <output hidden id="wertetabelle" aria-live="polite"></output>

  <fieldset id="plotter">
  <div aria-live="polite">
    <label id="f1">f<sub>1</sub>(x) = <input type="text" id="fkt1" value="sin(x)/x"></label>
    <label id="f2">f<sub>2</sub>(x) = <input type="text" id="fkt2" value="sin(x)"></label>
    <label id="f3">f<sub>3</sub>(x) = <input type="text" id="fkt3" value="cos(x)"></label>
  </div>
  <div>
    <label>xmin = <input lang="en" type="number" step="any" id="idxmin" value="-20"></label>
    <label>xmax = <input lang="en" type="number" step="any" id="idxmax" value="20"></label>
    <button id="plotbutton">Plotten</button>
  </div>
  </fieldset>

  <p id="vdeffkt">Vordefinierte Funktionen:
    <button>AM</button>,
    <button>PM</button>,
    <button>sin(x)/x</button>,
    <button>sinh</button>,
    <button>cosh</button>,
    <button>tanh</button> und
    <button>Puls</button>.
  </p>	

  <p id="jsfkt">Javascript unterstützt folgende Funktionen:
    <button>abs</button>,
    <button>acos</button>,
    <button>asin</button>,
    <button>atan</button>,
    <button>cos</button>,
    <button>exp</button>,
    <button>log</button>,
    <button>pow</button>,
    <button>sin</button>,
    <button>sqrt</button> und
    <button>tan</button>.
  Bitte den Definitionsbereich der Funktionen beachten.</p>
</body>
</html>