Funktionenplotter

Theoretische Überlegungen

In Turbo-CPP sind die Abmessungen und die Ausrichtung der Image-Komponente vorgegeben (0,0,Image.Width,Image.Height). Um die Berechnung des Funktionsgraphen zu vereinfachen, wird neben dem Bildbereich ein Weltbereich angenommen. Ein Weltpunkt WP hat die Weltkoordinaten WP.x, WP.y und der zugehörige Bildpunkt BP hat die Bildkoordinaten BP.x, BP.y. Der Nullpunkt der Bildkoordinaten liegt links oben, der Nullpunkt der Weltkoordinaten zunächst im Mittelpunkt des Fensters. Ein Weltpunkt WP ist durch die Klasse WPunkt definiert:

class WPunkt    //Weltpunkt (0/0) in Mitte
{double x,y;
};

Ein Bildpunkt BP ist durch folgende Klasse definiert:

class BPunkt     //Bildpunkt
{int x,y;
};

In der Umrechnung von Welt- in Bildkoordinaten und umgekehrt soll zunächst der Zoomfaktor eingerechnet werden. Im Programm soll dies durch die Werte zweier Trackbars oder Scrollbars realisiert werden. Hier werden der Einfachheit halber die Bezeichnungen zoomhz und zoomvt verwendet. Der Wert von zoomhz ist genau die Anzahl der Pixel im Bildbereich, die der Länge 1 auf der x-Achse im Weltbereich entsprechen (analog für zoomvt). Es ergeben sich somit folgende geometrische Verhältnisse:

zoomhz : 1 = (BP.x - Image→Width/2) : WP.x

zoomvt : 1 = (Image→Height/2 - BP.y) : WP.y

Somit ergibt sich folgender Zusammenhang, um die Bildkoordinaten (BP.x/BP.y) aus den Weltkoordinaten (WP.x/WP.y) zu berechnen:

BP.x :=zoomhz*WP.x +Image→Width/2)

BP.y := (Image→Height/2 -zoomvt*WP.y)

Analog kann man umgekehrt die Weltkoordinaten (WP.x/WP.y) aus den Bildkoordinaten (BP.x/BP.y) berechnen.

CBPunkt WeltZuBild(double x, double y)
{ //rechnet Weltkoordinaten in Bildkoordinaten um
  CBPunkt b;
  b.x=(int) (zoomhz*x+Form1->Image1->Width/2)+0.5;      //runden auf Ganze
  b.y=(int) (Form1->Image1->Height/2-zoomvt*y)+0.5;
  return b;
}
 
CWPunkt BildZuWelt(int x, int y)
{   //rechnet Bild in Weltkoordinaten um
	CWPunkt w;
	w.x=round((double)(x-Form1->Image1->Width/2)/zoomhz);
	w.y=round((double)(Form1->Image1->Height/2-y)/zoomvt);
	return w;
}

Programmcode

//---------------------------------------------------------------------------
 
#include <vcl.h>
#include <math.h>    //für die Wurzel sqrt()
#include <cmath>     //für abs()
#include <algorithm>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
 
TPoint punkt1=Point(60, 20), punkt2=Point(80,80);
 
 
int zoomhz=30,zoomvt=30, updohz=0,updovt=0;
 
double round(double x)
{
  x=(int)(x*100+0.5);
  x=(double)(x/100);
  return x;
}
 
class CBPunkt //Klasse Bildpunkt
{
  public: int x,y;
};
 
class CWPunkt //Klasse Weltpunkt
{
  public: double x,y;
};
 
CWPunkt wp;
CBPunkt bp;
 
CBPunkt WeltZuBild(double x, double y)
{ //rechnet Weltkoordinaten in Bildkoordinaten um
  CBPunkt b;
  b.x=(int) (zoomhz*x+Form1->Image1->Width/2)-updohz+0.5;      //runden auf Ganze
  b.y=(int) (Form1->Image1->Height/2-zoomvt*y)+updovt+0.5;
  return b;
}
 
CWPunkt BildZuWelt(int x, int y)
{   //rechnet Bild in Weltkoordinaten um
	CWPunkt w;
	w.x=round((double)(x-Form1->Image1->Width/2+updohz)/zoomhz);
	w.y=round((double)(Form1->Image1->Height/2-y+updovt)/zoomvt);
	return w;
}
 
class CQuadFunkt
{
   public:
   double a, b, c;
   double f(double x)  //Methode (Funktionswert)
   {  return a*x*x+b*x+c; }
   double diskr()
   {  return b*b-4*a*c;   }
   double nst1()
   {  return (-b+sqrt(diskr()))/(2*a);   }
   double nst2()
   {  return (-b-sqrt(diskr()))/(2*a);   }
   double extremum()
   {  return -b/(2*a); }
   void plot()
   { double diff;
	 CWPunkt wpmin, wpmax, wp;
	 CBPunkt bp;
	 wpmin=BildZuWelt(0,Form1->Image1->Height);  //li unten
	 wpmax=BildZuWelt(Form1->Image1->Width,0);   //re oben
	 //diff ist der Abstand im Weltbereich von Pixel zu Pixel
	 diff=(wpmax.x-wpmin.x)/Form1->Image1->Width;
	 wp.x=wpmin.x;
	 wp.y=f(wp.x);  //1. Weltpunkt ganz links
	 bp=WeltZuBild(wp.x,wp.y);  // 1. Bildpunkt ganz links
	 Form1->Image1->Canvas->MoveTo(bp.x,bp.y);
	 do
	 {
	   wp.x=wp.x+diff;   //x-Koordinate des nächsten Weltpunkts
	   wp.y=f(wp.x);
	   bp=WeltZuBild(wp.x,wp.y);
	   Form1->Image1->Canvas->LineTo(bp.x,bp.y);
	 } while (wp.x<=wpmax.x);
   }
};
 
CQuadFunkt quad;  //globale Variable, welche die quadratische Funktion und ihre
				  //Eigenschaften representiert
 
class CLinFunkt
{
   public:
   double k, d;
   double f(double x)  //Methode (Funktionswert)
   {  return k*x+d; }
   double nst()
   {  return (-d/k);   }
   void plot()
   { CWPunkt wpmin, wpmax;
	 CBPunkt bp;
	 wpmin=BildZuWelt(0,Form1->Image1->Height);  //li unten
	 wpmax=BildZuWelt(Form1->Image1->Width,0);   //re oben
	 wp.x=wpmin.x;
	 wp.y=f(wp.x);  //1. Weltpunkt ganz links
	 bp=WeltZuBild(wp.x,wp.y);  // 1. Bildpunkt ganz links
	 Form1->Image1->Canvas->MoveTo(bp.x,bp.y);
	 wp.x=wpmax.x;   //Weltpunkt ganz rechts
	 wp.y=f(wp.x);
	 bp=WeltZuBild(wp.x,wp.y);
	 Form1->Image1->Canvas->LineTo(bp.x,bp.y);
   }
};
CLinFunkt lin;  //globale Variable, welche die lineare Funktion und ihre
				//Eigenschaften representiert
 
void plotkoordkr()
{
  Form1->Image1->Canvas->Brush->Color=clWhite;
  Form1->Image1->Canvas->FillRect(Form1->Image1->Canvas->ClipRect);
 
 
 CWPunkt wpmin,wpmax;  //Hilfsweltpunkte
	//Zwei Eckpunkte des Bildbereichs
  wpmin=BildZuWelt(0,Form1->Image1->Height);  //li unten
  wpmax=BildZuWelt(Form1->Image1->Width,0);   //re oben
 
  //x-Koordinate
  CBPunkt bph1,bph2;  //Hilfspunkte
  bph1=WeltZuBild(wpmin.x,0);  //Anfangspkt x-Koord. im Bild
  bph2=WeltZuBild(wpmax.x,0);  //Endpkt x-Koord. im Bild
 
  Form1->Image1->Canvas->Pen->Color=clBlack;
  Form1->Image1->Canvas->MoveTo(bph1.x-2,bph1.y);
  Form1->Image1->Canvas->LineTo(bph2.x+2,bph2.y);
 
  //y-Koordinate
  bph1=WeltZuBild(0,wpmin.y);  //Anfangspkt x-Koord. im Bild
  bph2=WeltZuBild(0,wpmax.y);  //Endpkt x-Koord. im Bild
 
  Form1->Image1->Canvas->MoveTo(bph1.x,bph1.y+2);
  Form1->Image1->Canvas->LineTo(bph2.x,bph2.y-2);
 
  //Stricherl auf x-Achse
  for (int i=(int)(wpmin.x);i<=(int)(wpmax.x) ;i++ ) {
	bph1=WeltZuBild(i,0);
	Form1->Image1->Canvas->MoveTo(bph1.x,bph1.y-3);
	Form1->Image1->Canvas->LineTo(bph1.x,bph1.y+3);
  }
 
  //Stricherl auf y-Achse
	for (int i=(int)(wpmin.y);i<=(int)(wpmax.y) ;i++ ) {
	bph1=WeltZuBild(0,i);
	Form1->Image1->Canvas->MoveTo(bph1.x-3,bph1.y);
	Form1->Image1->Canvas->LineTo(bph1.x+3,bph1.y);
  }
 
  //Raster anzeigen
  if (Form1->CheckBoxRaster->Checked) {
     for (int i=(int)(wpmin.x);i<=(int)(wpmax.x) ;i++ ) {
	   for (int j=(int)(wpmin.y);j<=(int)(wpmax.y) ;j++ ) {
		bph1=WeltZuBild(i,j);
		Form1->Image1->Canvas->Pixels[bph1.x][bph1.y]=clBlack;
       }
	 }
  }
 
  //Achsenbeschriftung anzeigen
  //x-Koord
  bph1=WeltZuBild(wpmax.x,0);
  Form1->Image1->Canvas->TextOutA(bph1.x-10,bph1.y+5,"x");
  //y-Koord
  bph1=WeltZuBild(0,wpmax.y);
  Form1->Image1->Canvas->TextOutA(bph1.x-12,bph1.y,"y");
 
  //Zahlenwerte auf Achse 1er
  bph1=WeltZuBild(1,0);
  Form1->Image1->Canvas->TextOutA(bph1.x-2,bph1.y+5,"1");
  bph1=WeltZuBild(-1,0);
  Form1->Image1->Canvas->TextOutA(bph1.x-4,bph1.y+5,"-1");
  bph1=WeltZuBild(0,1);
  Form1->Image1->Canvas->TextOutA(bph1.x-10,bph1.y-7,"1");
  bph1=WeltZuBild(0,-1);
  Form1->Image1->Canvas->TextOutA(bph1.x-13,bph1.y-7,"-1");
 
 
  //Zahlenwerte 5er
  //x-Achse +
  for (int i=5;(i<=wpmax.x) ||(i<=wpmin.x*(-1))||(i<=wpmin.y*(-1))||(i<=wpmax.y) ;i=i+5 ) {
	bph1=WeltZuBild(i,0);
	Form1->Image1->Canvas->TextOutA(bph1.x-3,bph1.y+5,IntToStr(i));
	bph1=WeltZuBild(-i,0);
	Form1->Image1->Canvas->TextOutA(bph1.x-7,bph1.y+5,IntToStr(-i));
	bph1=WeltZuBild(0,i);
	Form1->Image1->Canvas->TextOutA(bph1.x-12,bph1.y-7,IntToStr(i));
	bph1=WeltZuBild(0,-i);
	Form1->Image1->Canvas->TextOutA(bph1.x-15,bph1.y-7,IntToStr(-i));
  }
}
 
void FunktEigensch()
{
if (Form1->RadioButtonquad->Checked) {
	Form1->GroupBox2->Caption="Quadratische Funktion";
		Form1->LabelExtremum->Caption="Extremum:";
	if (quad.diskr()<0) {
		Form1->LabelNSTAnzahl->Caption="Es gibt keine Nullstelle!";
		Form1->LabelN1->Caption="";
		Form1->LabelN2->Caption="";
	} else
	if (quad.diskr()==0) {
		Form1->LabelNSTAnzahl->Caption="Es gibt genau eine (doppelte) Nullstelle!";
		Form1->LabelN1->Caption="x = "+ FloatToStr(quad.nst1());
		Form1->LabelN2->Caption="";
	} else
	if (quad.diskr()>0) {
		Form1->LabelNSTAnzahl->Caption="Es gibt zwei Nullstellen!";
		Form1->LabelN1->Caption="x1 = "+ FloatToStr(quad.nst1());
		Form1->LabelN2->Caption="x2 = "+ FloatToStr(quad.nst2());
	}
	if (quad.a>0) {
	   Form1->LabelEx->Caption="Tiefpunkt T("+FloatToStr(quad.extremum())+"/"+FloatToStr(quad.f(quad.extremum()))+")";
	} else
	   Form1->LabelEx->Caption="Hochpunkt H("+FloatToStr(quad.extremum())+"/"+FloatToStr(quad.f(quad.extremum()))+")";
 
 
} else
{
	Form1->GroupBox2->Caption="Lineare Funktion";
	if (lin.k!=0) {
		Form1->LabelNSTAnzahl->Caption="Es gibt eine Nullstelle!";
		Form1->LabelN1->Caption="x = "+ FloatToStr(lin.nst());
		Form1->LabelN2->Caption="";
	} else
	{
		if (lin.d=0) {
			Form1->LabelNSTAnzahl->Caption="Es gibt unendlich viele Nullstellen!";
		} else {
			Form1->LabelNSTAnzahl->Caption="Es gibt keine Nullstelle!";
		}
		Form1->LabelN1->Caption="";
		Form1->LabelN2->Caption="";
 
	}
	Form1->LabelEx->Caption="";
	Form1->LabelExtremum->Caption="";
}
}
 
void plotfunktion()
{
  plotkoordkr();
  if (Form1->RadioButtonquad->Checked)
  {
   quad.a=StrToFloat(Form1->Edita->Text);
   quad.b=StrToFloat(Form1->Editb->Text);
   quad.c=StrToFloat(Form1->Editc->Text);
   quad.plot();
  } else
  {
	lin.k=StrToFloat(Form1->Editk->Text);
	lin.d=StrToFloat(Form1->Editd->Text);
	lin.plot();
  }
  FunktEigensch();
}
 
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
plotfunktion();
 
 
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift,
	  int X, int Y)
{
CWPunkt w;
w=BildZuWelt(X,Y);
Label1->Caption=FloatToStr(w.x);
Label2->Caption=FloatToStr(w.y);
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::FormCreate(TObject *Sender)
{
plotfunktion();
FunktEigensch();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
zoomhz=TrackBar1->Position;
plotfunktion();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::TrackBar2Change(TObject *Sender)
{
zoomvt=TrackBar2->Position;
plotfunktion();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::CheckBoxRasterClick(TObject *Sender)
{
plotfunktion();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::UpDownhzClick(TObject *Sender, TUDBtnType Button)
{
updohz=UpDownhz->Position;
plotfunktion();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::UpDownvtClick(TObject *Sender, TUDBtnType Button)
{
updovt=UpDownvt->Position;
plotfunktion();
}
//---------------------------------------------------------------------------
 
 
void __fastcall TForm1::EditaExit(TObject *Sender)
{
if (Edita->Text==""||Edita->Text=="0") {
   Edita->Text="1";
}	
}
//---------------------------------------------------------------------------
 
 
void __fastcall TForm1::EditbKeyPress(TObject *Sender, char &Key)
{
if (!((Key>='0')&&(Key<='9')||(Key==',')||(Key==8)||(Key=='-')))
 {Key=0;
 }
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::EditcKeyPress(TObject *Sender, char &Key)
{
if (!((Key>='0')&&(Key<='9')||(Key==',')||(Key==8)||(Key=='-')))
 {Key=0;
 }	
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::EditkKeyPress(TObject *Sender, char &Key)
{
if (!((Key>='0')&&(Key<='9')||(Key==',')||(Key==8)||(Key=='-')))
 {Key=0;
 }	
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::EditdKeyPress(TObject *Sender, char &Key)
{
if (!((Key>='0')&&(Key<='9')||(Key==',')||(Key==8)||(Key=='-')))
 {Key=0;
 }	
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::EditaKeyPress(TObject *Sender, char &Key)
{
if (!((Key>='0')&&(Key<='9')||(Key==',')||(Key==8)||(Key=='-')))
 {Key=0;
 }
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::RadioButtonquadClick(TObject *Sender)
{
plotfunktion();
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::RadioButtonlinClick(TObject *Sender)
{
plotfunktion();
}
//---------------------------------------------------------------------------