Ein eigenes Koordinatensystem



  • Hallo zusammen,
    da ich häufig mit Wertetabellen arbeite, möchte ich natürlich auch mit Graphen arbeiten. Bis jetzt habe ich immer umständlich mit einem Image gearbeitet, doch gibt ist dieses eher unhandlich:

    1.) Der Nullpunkt ist oben links.
    2.) Es ist schwierig (oder vielleicht auch unmöglich) den Graphen anzupassen (Dicke)
    3.) Jedes Image muss individuell an den Graphen angepasst werden, da ein Schritt gleich 1 Pixel ist.

    Ich möchte nun eine eigene Komponente schreiben, sowas wie TKoordinatensystem. Da ich jedoch nocht nicht so viel Praxiserfahrung gesammelt habe, benötige ich eure Hilfe. Könnt ihr mir einen Rat geben, wie ich am besten an meinen Plan angehe. Wie könnte ich am besten erreichen, dass mein Koordinatensystem

    1.) x,y - Achse hat (anpassungsfähiger waagerechter und senkrechter Schritt mit jeweiligen Unterteilungen)
    2.) Je nach Funktion soll sich das Koordinatensystem anpassen, also die Schrittweite beispielsweise ändern
    3.) Der Graph soll farblich (oder auch bei der Dicke) beeinflusst werden
    4.) Und ganz wichtig: Der Nullpunkt soll in der Mitte liegen.

    Nicht, dass wir uns falsch verstehen. Ich möchte nicht, dass ihr mir ein Programm schreibt, doch brauche ich ein paar Hinweise und Tipps, da ich wie gesagt noch nicht so viel Praxiserfahrung mit dem Builder habe!

    Ich möchte nicht mir Komponenten wie PerformanceGraph etc. arbeiten, sondern wollte eigentlich eine eigene erstellen

    Vielen Dank und bis bald
    lg, freakC++



  • Hallo

    Allgemeine Infos für das Implementieren einer eigenen Komponente findest du hier.

    Um TCanvas oder die entsprechenden WinAPI-Funktionen (auf denen TCanvas aufbaut) wirst du jedenfalls nicht umhinkommen. Damit sind auch deine konkreten Fragen schnell beantwortet :
    1,2 und 4 : Das must du immer noch umrechnen lassen. Natürlich kannst du diese Umrechnung so in deiner Komponente implementieren, das nach außen hin für den Benutzer nur ein paar Eigenschaften einzustellen sind, und die Rohdaten übergeben kann
    3 : TCanvas bietet die Möglichkeiten, sowohl Farbe als auch Dicke von Linien zu beeinflußen

    bis bald
    akari



  • Den NULL Punkt kann man mittels:

    SetViewportOrgEx(Canvas->Handle, Width/2, Height/2, NULL);
    Canvas->Ellipse(-100, -100, 100, 100);
    

    ins Zentrum verschieben.

    bzw per:

    SetMapMode(Canvas->Handle, MM_LOENGLISH);
    

    kann man die Y Richtung tauschen.



  • Hallo,
    danke für eure Antworten. Ich habe mir jetzt mal die von akari gepostete Seite ein bisschen näher gebracht und dabei sind mir folgende Fragen aufgekommen:

    1.) Von welcher Klasse sollte ich am besten ableiten? Ich dachte ursprünglich an TCanvas, weil ich ja eigentlich nur zeichnen möchte, wobei das in einem Koordinatensystem geschehen soll. Nachdem ich mir die Einführung durchgelesen habe, käme vielleicht auch TGraphicControl in Frage, weil ein Koordinatensystem ja "keinen Eingabefokus hat". Was ist hier besser?

    2.) Ich habe noch nicht so ganz begriffen, wie ich das Visuelle mache. Ich brauche ja irgendeine Fläche, auf der das Koordinatenystem erscheinen soll. Ich erstelle ja nicht sowas wie TTimer. Arbeitet man da mit TRect oder wie muss ich hier rangehen?

    Ich habe nämlich diese Seite hier gefunden:

    http://functionx.com/bcb/gdi/gdicoord.htm

    die schon einen ziemlich guten Einsteig gibt und teilweise auch den Code von VergissEs verwendet. Dort wird halt nur auf der Form gearbeitet, weshalb ich Frage Nr.2 habe

    Das wären erstmal meine wichtigsten Fragen, wobei sicherlich noch einige hinzukommen werden 😃 .

    Vielen Dank :xmas1:
    lg, freakC++



  • Ich denke, die beste Komponente wäre TPaintBox (das schon von TGraphicControl abgeleitet ist), so daß du dann direkt im Paint-Event (auf dessen Canvas) zeichnen kannst.

    Edit: habe ich bei meinem Graph-Programm auch so gemacht (d.h. Anzeige einer mathematischen Formel): http://www.bitel.net/dghm1164/downloads/Mathematik.zip



  • Ja, genau....sowas wie "Graph.exe" möchte ich auch machen. Hast Du das auch so ähnlich gemacht, wie auf meiner geposteten Seite. Ich möchte aber halt noch eine eigene KOmponente machen. Bei dir ist der Graph auf dem Formular, doch ich weiß halt noch nicht, wie ich diese Fläche machen soll...bzw, wie diese zu definiren ist.

    Wenn ich von TPaintBox ableite, dann habe ich auch TCanvas?

    Vielen Dank für eure Hilfe
    lg, freakC++



  • Hallo

    Ja, TPaintBox hat ein TCanvas, Dimensionen sind automatisch wie TPaintBox. Im OnPaint-Event der TPaintBox must du jeweils einmal komplett das gesamte Ko.-system samt Daten zeichnen. Das Zeichnen selber ist genauso wie ohne eigene Komponente.

    bis bald
    akari



  • Mmmh...vielen Dank. Ich dachte daran, dass das Programm ein zweidimensonales Array an die KOmponenten übergibt, und diese dann den Graphen zeichnet. Ein Zweidimensionales Array, weil der eine Teil die x WErte und der andere Teil die y Werte hat. Naja, ich bin jetzt bis MOntag weg, doch dann muss ich nochmal wegen der Komponentenfläche fragen. Das ist mir jetzt noch nicht 100 % klar.

    Ich sitze jetzt einige Stunden im Auto und werde jetzt mal einiges versuchen 😉

    Vielen Dank euch drei!
    Bis Montag
    lg, freakC++



  • Hier mal meine Graphen-Zeichnen-Funktion:

    // Header
    typedef double (__closure *GraphFct)(double x);
    void DrawGraph(TCanvas *cv, double xMin, double xMax, double yMin, double yMax,
    				GraphFct fct, TColor clGraph=clRed, TColor clAxis=clBlack,
    				TColor clCanvas=clWhite);
    
    // Source
    void DrawGraph(TCanvas *cv, double xMin, double xMax, double yMin, double yMax,
    				GraphFct fct, TColor clGraph, TColor clAxis, TColor clCanvas)
    {
    	const int XOffset = 0,
    			  YOffset = 0;
    
    	TRect &r = cv->ClipRect;
    	int nX0 = XOffset;
    	int nWidth = (r.Right-r.Left+1) - nX0;
    	int nY0 = YOffset;
    	int nHeight = (r.Bottom-r.Top+1) - nY0;
    
    	double kx = nWidth / (xMax - xMin);
    	double ky = nHeight / (yMin - yMax);
    	int nXZero = nX0 + kx * -xMin;
    	int nYZero = nY0 + ky * -yMax;
    
    	// Canvas löschen
    	TPen *p = cv->Pen;
    
    	p->Color = clCanvas;
    	cv->FillRect(r);
    
    	p->Color = clAxis;
    	// x-Achse
    	cv->MoveTo(nX0, nYZero);
    	cv->LineTo(nX0 + nWidth, nYZero);
    
    	const int X = 5, YPixel = 2;
    	double dxStep = (xMax - xMin) / (X-1);
    	double dx=xMin;
    	int nx;
    	AnsiString sx;
    
    	for(int i=0; i<X; i++)
    	{
    		nx = kx * (dx - xMin);
    		sx = FloatToStr(dx);
    		cv->TextOut(nX0 + nx, nYZero+YPixel, sx);
    		dx += dxStep;
    	}
    
    	// y-Achse
    	cv->MoveTo(nXZero, nY0);
    	cv->LineTo(nXZero, nY0 + nHeight);
    
    	const int Y = 5, XPixel = 2;
    	double dyStep = (yMax - yMin) / (Y-1);
    	double dy=yMin;
    	int ny;
    	AnsiString sy;
    
    	for(int i=0; i<Y; i++)
    	{
    		ny = ky * (dy - yMax);
    		sy = FloatToStr(dy);
    		cv->TextOut(nXZero+XPixel, nY0 + ny, sy);
    		dy += dyStep;
    	}
    
    	// Funktionswerte pro x berechnen
    	double xStep = (xMax - xMin) / nWidth;
    	double x = xMin, y;
    	int nX, nY;
    	bool bFirst = true;
    
    	p->Color = clGraph;
    	for(nX=0; nX<nWidth; nX++)
    	{
    		y = fct(x);
    		if(y < yMin || y > yMax)
    			bFirst = true;
    		{
    			nY = ky * (y - yMax);
    			if(bFirst)
    			{
    				cv->MoveTo(nX0 + nX, nY0 + nY);
    				bFirst = false;
    			}
    			else
    				cv->LineTo(nX0 + nX, nY0 + nY);
    		}
    		x += xStep;
    	}
    }
    

    Kannst ja mal als Vergleich nehmen... ich benutzte aber direkt eine Funktion anstatt ein zwei-dim. Array.

    P.S. Um die Dicke der Linien zu ändern, einfach Pen->Width setzen...



  • Moin, moin!

    Da bin ich wieder. Ich bedanke mich bei Th69 ganz herzlich für den Code. Ich werde sicherlich ein paar Ideen daraus entnehmen können. Ihr habt mir schon sehr geholfen und ich habe schon eine klare Vorstellung wie meine Komponente auszusehen hat. Ich möchte nämlich, dass mein Graph in der Komponentenpalette erscheint. Ich habe das vor langer Zeit schonmal gemacht, doch handelte es sich dabei nur um einen runden Button, dessen Fläche ich mit einer Ellipse gezeichnet habe.

    Meine letzte Hürde ist, dass ich nicht genaus weiß, wie ich diese Fläche hinbekommen soll. Ich möchte also dann meine Komponente aus der Palette wählen und dann auf der Form aufziehen. Diese soll natürlich keinen Rand haben, sondern nur ein Koordinatenkreuz. Optisch gleicht sie also TImage, nur halt mit dem Kreuz. Hier liegt mein Problem. Ich weiß nicht, wie ich diese Fläche hinbekomme. Ich kann ja kein Rechteck zeichnen und das dann als meine Komponentenfläche nehmen, weil dieses Ränder hat. Ich hoffe, dass ihr mein Anliegen versteht.

    Vielen Dank für euer Bemühen!
    lg, freakC++



  • Wie schon mehrfach geschrieben, leite von TPaintBox ab und male im Paint-Ereignis (eine TPaintBox ist ganz leer, hat also keinen sichtbaren Rand!).



  • Hi,
    sry für die blöde Frage. Irgendwie war mir nicht mehr klar, dass bei dieser Vererbung die Fläche ja gleich dabei ist. Naja. Das wären erstmal meine Fragen. Es werden aber sicherlich noch ein paar hinzugekommen, weshalb ich mich wahrscheinlich später wieder melden werde.

    Viele Dank und bis bald
    lg, freakC++



  • Hallo,
    da ist sie schon. Die nächste Frage. Irgendwie stehe ich nämlich gerade auf dem Schlauch. Wie bereits gesagt, möchte ich, dass beim Erstellen meiner Komponente ein Koordinatenkreuz angezeigt wird. Ich habe über "Komponente -> Neue Komponente" eine neue Komponente erzeugen lassen. NUn habe ich einfach in den Konstruktor der Komponente TGraph folgendes geschrieben:

    Canvas->Pen->Color = clBlack;
     Canvas->MoveTo(ClientWidth/2, 0);
     Canvas->LineTo(ClientWidth/2, ClientHeight);
     Canvas->MoveTo(0, ClientHeight/2);
     Canvas->LineTo(ClientWidth, ClientHeight/2);
    

    Die Syntax ist zwar korrekt, doch ein Koordinatenkreuz erscheint nicht. Ich habe die Komponente installiert und auf meine Form gezogen. Wisst ihr da Bescheid?

    edit: Wie kann ich eigentlich eine Komponente wieder aus der Komponentenpalette löschen?

    Vielen Dank
    lg, freakC++



  • freakC++ schrieb:

    Wie kann ich eigentlich eine Komponente wieder aus der Komponentenpalette löschen?

    Indem du die Package-Projektdatei (in aller Regel eine *.bpk) öffnest und die betreffende Unit aus dem Projekt
    entfernst, dann das Package neu erstellst.

    Dein Code funktioniert nicht im Konstruktor, da dort i.d.R. nur Initialisierungen vorgenommen werden. Schreibs in ein
    Event.

    mfg
    kpeter



  • Klar, hab' ich nicht mehr parat gehabt. Da ich mit dem Builder solche Dinge noch nicht oft gemacht habe, benötige ich da nochmal Hilfe. Ich habe jetzt hier meine Komponente. Da ich sie von TPaintBox abgeleitet habe, erscheint im Objektinspektor das Ergeigniss OnPaint. Soweit ich weiß, muss ich dieses jetzt angleichen, also den Code für das Koordinatenkreuz schreiben, damit dieses sofort beim Programmstart erscheint und sich ggf. angleicht. Das Problem ist nur, dass ich nicht weiß, wie "ich in ein Event" schreibe oder diese Ereigniss angleiche, sodass es dauerhaft ich der Komponente verankert bleibt.

    Könnt ihr mir sagen, wie ich das mache? Das wäre klasse!

    Vielen Dank
    lg, freakC++



  • Ja genau, im OnPaint-Eregnis!

    Und falls du nicht weißt, wie man ein Ereignis dynamisch (d.h. im Code) registriert, dann schau in die FAQ: http://www.c-plusplus.net/forum/viewtopic-var-t-is-39206.html



  • Hallo,
    vielen Dank für den Hinweis auf die FAQ. Ich habe jedoch nicht den Eindruck, dass mir der Artikel weiterhilft, weil hier das Event ja in einem ganz bestimmten Programm einmal zugewiesen wird. Ich möchte aber, dass ich an der Komponente selbst - unabhängig vom Projekt - gearbeitet wird.

    Ich habe hier den Quelltext meiner TGraph Klasse, die ich als KOmponente in die Komponentenpalette gestopft habe. Ich kann der FAQ nich entnehmen, wie ich mein Graph.cpp ändern muss, damit immer ein Koordinatenkreuz auf die Fläche gemalt wird, wenn ich ein neues Objekt auf das Formular ziehe.

    Ich habe mir nochmal das OnPaint Ereigniss angeschaut, doch wenn ich das hier schreibe:

    void __fastcall TForm1::Graph1Paint(TObject *Sender)
    {
     Canvas->Pen->Color = clBlack;
     Canvas->MoveTo(ClientWidth/2, 0);
     Canvas->LineTo(ClientWidth/2, ClientHeight);
     Canvas->MoveTo(0, ClientHeight/2);
     Canvas->LineTo(ClientWidth, ClientHeight/2);
    }
    

    dann wird auf das Formular gezeichnet und nicht in meine Komponente. Wenn ich noch "Graph1->" vor jede Zeile schreibe, dann erscheint gar nichts. Ich will die Komponente dauerhaft bearbeiten, damit sofort das Koordinatenkreuz erscheint, wenn ich diese aus der Komponentenpalette auf das Formular ziehe und das Programm starte. Das kriege ich - trotz FAQ - nicht hin. Könnt ihr mir da weiterhelfen?

    Vielen Dank
    lg, freakC++



  • Hallo

    Die Eventmethode muß eben in die neue Komponente integriert werden, nicht ins Form.

    // Konstruktor
    __fastcall TGraph::TGraph(...)
    {  
      OnPaint = DoPaint; // Event wird durch Quellcode zugewiesen
    }
    
    // Eventmethode für TGraph
    void __fastcall TGraph::DoPaint(TObject *Sender)
    {
     Canvas->Pen->Color = clBlack;
     Canvas->MoveTo(ClientWidth/2, 0);
     Canvas->LineTo(ClientWidth/2, ClientHeight);
     Canvas->MoveTo(0, ClientHeight/2);
     Canvas->LineTo(ClientWidth, ClientHeight/2);
    }
    

    bis bald
    akari



  • freakC++, du stellst dich wirklich manchmal etwas "unbeholfen" an (habe ich auch schon bei deinen anderen Posts gesehen). Deine armen Lehrer, wenn du dich in der Schule auch so anstellst...

    Du mußt beim Programmieren schon die Fähigkeit entwickeln, etwas abstrakter zu denken und nicht nur vorgefertigte Lösungen zu erwarten.

    Wie Dieter Nuhr schon sagt: "erst Hirn einschalten" -)

    Noch als Hinweis zu akari's Code: die Deklaration für die DoPaint-Methode mußt du dann natürlich auch noch in die entsprechende Klassendefinition (in die Header-Datei) packen.



  • Hallo akari und Th69,
    vielen Dank für die Hilfe. Ich habe das Problem nun lösen können. Ihr seit top:

    @Th69: Ich verstehe, dass Du den Anschein von mir hast, dass ich manchmal keine Lust oder vielleicht auch nicht in der Lage bin, mir selbst zu helfen. Ich gebe zu, dass meine Posts für euch manchmal grenzwertig sind. Doch dann beantworte mir doch bitte die folgende Frage:

    Ich bringe mir C++ aus reiner Leidenschaft durch Bücher bei und habe keinen, den ich fragen kann. Da mir häufig von euch gesagt worden ist - und ich euch da sehr vertraue - erstmal Grundlagen zu lernen, arbeite ich erstmal Grundlagenbücher durch. Da steht aber noch lange nichts vom Builder drin. Wie hast Du das gelernt und wie soll ich es lernen, damit ich solche Fragen verhindern kann. Ich persönlich lerne dadurch am besten, indem ich lese, ausprobiere und nachfrage. Vielleicht hast Du ja eine bessere Strategie. Ich kann ich dir jedoch versichern, dass ich mich in der Schule nicht so anstelle, da ich dort Lehrer habe, ich ich ansprechen kann. Neben meinen Büchern seit ihr meine Lehrer, die ich fragen kann!

    Ich hoffe wirklich, dass ich mich nicht unbeliebt mache, doch wie soll ich sonst lernen, als immer wieder zu lesen, auszuprobieren und nachzufragen. Wenn ihr Anregunggen habt, dann her damit! Ich bin euch jedoch sehr dankbar.

    Eine Frage noch: Wo kann ich denn nachlesen (denn ich bin durchaus willig, eigenständig zu arbeiten) oder wie kann ich erfahren, wie ich Eigenschaften in den Objektinspektorhinzufügen kann, z.B. xMax oder yMax meines Koordinatensystems. Dazu gibt die Suchfunktion und auch Google keine Informationen (ich habe jedoch keine gefunden). In meinen Büchern steht dazu nichts, deswegen gilt auch hier wieder: Woher soll ich das denn wissen? Nachfragen ist hoffentlich nicht verboten und ich hoffe, dass ich auch in Zukunft fragen darf, auch wenn es vielleicht für euch Pille-Palle ist.

    Vielen Dank für die Hilfe, Th69!
    lg, freakC++


Anmelden zum Antworten