Ein eigenes Koordinatensystem



  • Ich habe nochmal ein bisschen ausprobiert. Wenn ich folgendes in die DoPaint Methode schreibe:

    SetViewportOrgEx(TGraph::Canvas->Handle, Width/2, Height/2, NULL);
     SetMapMode(TGraph::Canvas->Handle, MM_LOENGLISH);
    Canvas->LineTo(200,200);
    

    dann wird die Linie nicht vom gewünschten Nullpunkt gezeichnet. Wenn jedoch das schreibe:

    SetViewportOrgEx(TGraph::Canvas->Handle, Width/2, Height/2, NULL);
     SetMapMode(TGraph::Canvas->Handle, MM_LOENGLISH);
     TGraph::Canvas->LineTo(200,200);
    

    dann wird die Linie "korrekt" gezeichnet. Wenn ich dann jedoch einen Button erstelle mit dem Code

    Graph1->Canvas->LineTo(100,100);
    

    dann wird nicht vom gewünschten Nullpunkt gezeichnet. Verliert die Verschiebung des Ursprungs da ihre Wirkung oder was muss ich machen, damit dieser immer verschoben bleibt?

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

    PS.: SOll ich eigentlich für solche Fragen einen neuen Thread aufmachen, da das ja mit dem Anfangsthema nichts mehr zu tun hat?



  • Hallo

    Ich habe keine Erfahrung mit dem SetViewport..., aber in deinem Code ist das "Graph1->" schonmal falsch. Nocheinmal : Für deine neue Komponente müßen alle Zeichenoperationen in der TGraph::DoPaint-Methode erfolgen. Dazu steht dir die Eigenschaft Canvas zur Verfügung. Du brauchst auch nicht "TGraph::" zu benutzen. "Graph1->" ist aber gar ein Fehler. Denn in der DoPaint-Methode gibt es keine konkrete Graph1-Instanz, du schreibst doch schließlich eine allgemeine, mehrfach verwendbare Komponente.

    Noch passt das in diesen Thread...

    bis bald
    akari



  • Nein, das "Graph1->" steht in einer ButtonClick Methode. Das war ein Text, um zu überprüfen, ob nun der Nullpunkt endlich korrekt verschoben worden ist. Leider hat dieser Test halt ergeben, dass dies nicht der Fall ist. Dennoch wird die Linie, die in der DoPaint Methode gezeichnet wird, korrekt gezeichnet (also ab dem gewünschten NullPunkt in der Mitte meiner KOmponente).

    Ich verstehe also nicht, warum ich auf mein Graph Objekt nicht ab dem in der DoPaint-Methode festgelegten Nullpunkt zeichnen kann.

    Wer weiß da weiter?

    Vielen Dank, akari
    lg, freakC++



  • Hallo

    Nocheinmal : Für deine neue Komponente müßen alle Zeichenoperationen in der TGraph::DoPaint-Methode erfolgen

    Egal wo du diese OnButton-Methode definiert hast, dort ist nunmal der Zugriff auf TGraph::Canvas nicht korrekt. Wenn dann solltest du von dem Button aus eine von deinen für TGraph neu implementierten Eigenschaften setzen. Mit TGraph::Refresh() löst du dann das Paint-Event aus, in dem dann die Eigenschaften ausgewertet werden und die Zeichenoperationen ausgeführt werden.

    bis bald
    akari



  • Aber ich habe den Button auf meinem Forumar und habe ich eine Instanz von TGraph erstellt, auf die zeichnen möchte. Diese Zeichenanweisung steht in einem Button.

    Naja, egal. Auf jeden Fall stimmt dieser Code nicht und ich weiß einfach nicht warum:

    void __fastcall TGraph::DoPaint(TObject *Sender)
    {
     SetViewportOrgEx(Canvas->Handle, Width/2, Height/2, NULL);
     SetMapMode(Canvas->Handle, MM_LOENGLISH);
     Canvas->MoveTo(0,Height/2);
     Canvas->LineTo(0,-Height/2);
    }
    

    NUn sollte wenigstens die Y Achse gezeichnet werden, doch diese ist einfach nicht in der Mitte und sie geht noch nichtmal durch die ganze Graph KOmponente.
    Könnt ihr mir da bitte weiterhelfen?

    Vielen DAnk :xmas1:
    lg, freakC++



  • Du musst nach dem zeichnen den Urzustand wieder herstellen.

    //Hintergrund mit Gradient überzeichnen
    	GradientFillCanvas(Canvas, clWhite, clBlue, ClientRect, gdVertical);
    
    	SetViewportOrgEx(Canvas->Handle, Width/2, Height/2, NULL);
    
    	Canvas->Pen->Style = psSolid;
    	Canvas->Pen->Color = clBlack;
    
    	//Horizontale Linie
    	Canvas->MoveTo(0, Height/2);
    	Canvas->LineTo(0, -Height/2);
    
    	//Vertikale Line
    	Canvas->MoveTo(Width/2, 0);
    	Canvas->LineTo(-Width/2, 0);
    
    	//Urzustand wieder herstellen
    	SetViewportOrgEx(Canvas->Handle, 0, 0, NULL);
    


  • Hey VergissEs,
    vielen Dank für deine Hilfe. Zwei Anliegen habe ich:

    1.) Was ist GradientFillCanvas und der Parameter gdVertica. Die Hilfefunktion und Google kennen beides nicht (google verweist nur auf dich :D). Was muss ich machen, um diese Funktion zu nutzen.

    2.) KÖnntest Du mir nochmal genau sagen, warum ich den Urzustand wiederherstellen muss. Was genau ist der Urzustand? Warum kann ich in der Do-Paint Methode nicht einfach den Nullpunkt ändern und dann eine Linie zeichnen. Diese Linie würde nicht vom Zentrum meiner KOmponente gehen.

    Bitte helft mir, auch wenn das schon ein riesen Thread ist. Ich gebe zu, dass Th69 Aussage hier stimmt: Ich bin nämlich momentan ein bisschen unbeholfen und weiß einfach nicht, wie das Problem lösen kann (also dass ich dauerhaft den Ursprung verschieben kann).

    Ich danke euch sehr!
    lg, freakC++



  • freakC++ schrieb:

    1.) Was ist GradientFillCanvas und der Parameter gdVertica. Die Hilfefunktion und Google kennen beides nicht

    ➡ hier



  • Ok, danke. Das werde ich mir mal anschauen. Doch wozu ist diese "Gradientfüllung" hier nützlich. Außerdem:

    Muss ich immer den Nullpunkt vor jeder Zeichnung neu verschieben. Ist eine Verschiebung nur temporär? Warum kann ich diesen nicht dauerhaft verschieben?

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

    PS.: Ich mach morgen mal einen neuen Thread auf, weil sich dieser sehr aufbläht, keine richtige Überschrift hat und gar nichts mehr mit dem Anfangsthema zu tun hat. Dann haben es andere leichter ihn über die Suchfunktion zu finden. Ich hoffe das ist ok, akari?



  • freakC++ schrieb:

    Muss ich immer den Nullpunkt vor jeder Zeichnung neu verschieben. Ist eine Verschiebung nur temporär? Warum kann ich diesen nicht dauerhaft verschieben?

    Hallo,

    wenn du die gepostete Funktion von Th69 genau betrachtest, hast du schon die Lösung.
    Bei gleichen Werten für (zB) xmin = -5 und xmax = 5, ebenso für die Y-Achse ist dein System zentriert.

    Naja, wird schon 😉 .

    mfg
    kpeter



  • Hallo,

    Naja, wird schon .

    Das hoffe ich auch, doch langsam glaube ich nicht mehr an mich. Auch wenn ihr es schon gesagt habt, dann schaut euch doch bitte dieses Beispiel mal an. Vielleicht verstehe ich euch falsch, vielleicht verstehe ich die Funktion SetViewportOrgEx nicht, vielleicht bin ich einfach zu doof.
    Ich habe wie schon gesagt, akaris Do-Point Methode und eine Methode "Nullpunkt", die nichts anderes macht, außer den Nullpunkt zu verschieben:

    void __fastcall TGraph::Nullpunkt()
    
    {
     HDC hDC = Canvas->Handle;
     SetMapMode(hDC, MM_TEXT);
     SetViewportOrgEx(Canvas->Handle,ClientWidth/2, ClientHeight/2, NULL);
     Canvas->MoveTo(0,0);
    }
    //---------------------------------------------------------------------------
    void __fastcall TGraph::DoPaint(TObject *Sender)
    {
     xmin = -Width/2;
     xmax =  Width/2;
     ymin = -Height/2;
     ymax =  Height/2;
    
     Nullpunkt();
     Canvas->Pen->Style = psSolid;
     Canvas->Pen->Color = clBlack;
     Canvas->MoveTo(0, Height/2);    //das kommt auch von euch ;)
     Canvas->LineTo(0, -Height/2);
     Canvas->MoveTo(Width/2, 0);
     Canvas->LineTo(-Width/2, 0);
     SetViewportOrgEx(Canvas->Handle, 0, 0, NULL); //Wiederherstellung der urprünglichen Nullpunktes
    }
    

    Wenn ich nun eine Instanz von TGraph auf mein Formular ziehe, dann wird das Koordinatenkreuz schön gemalt und alles läuft super. Jetzt kommt das für mich völlig "Unlogische" und ich bitte wirklich um eure Geduld, denn ich will was lernen:

    Ich habe mir eine eigene LineTo Methode geschrieben, die vor einer Zeichnung den Nullpunkt verschiebt.

    void __fastcall TGraph::Linie(int a, int b)
    {
     Nullpunkt();  //Verschiebung des Nullpunkts und zum Nullpunkt gehen
     Canvas->LineTo(a,b); //ab dem Nullpunkt zeichnen
     SetViewportOrgEx(Canvas->Handle, 0, 0, NULL); //wieder den ursprünglichen Nullpunkt herstellen
    }
    

    Nun habe ich das mal ausprobiert. Ich habe einen Button und eine Instanz von TGraph auf meinem Formular und die Anweisung in meinem Button lautet:

    Graph1->Linie(200,200);
    

    Wenn ich nun auf meinem Buttton klicke, wird zwar eine Linie gezeichnet, aber nicht vom gewünschten Ursprung (also dort wo sich x und y Achse schneiden). Hier liegt mein Problem, das ich trotz eines riesen Thread uns massig Antworten nicht zu lösen vermag. Wenn ich meine TGraph Instanz auf dem Formular verschiebe, dann erscheint bei Knopfdruck an anderer Stelle die Linie.

    Entweder habe ich irgendwas grundsätzlich nicht verstanden oder ich übersehe die ganze Zeit etwas. Bitte helft mir nochmals und erklärt mir, wie ich richtig zeichnen Kann.

    Danke!

    lg, freakC++, der langsam verzweifelt und sich schon sehr doof fühlt



  • Wiso willst du von außerhalb in den Graphen zeichnen?

    Wiso nicht einfach die Zeichenkoordinaten übergeben und die Komponente kümmert sich um das zeichnen im OnPaint.

    Wenn du wiso von ausserhalb auf die neu erstellte Komponete zeichnen willst, wiso erstellst du überhaupt eine Komponente? ... kannst ja auch gleich die PaintBox selbst verwenden und alles im OnPaint Zeichnen lassen und evtl. später
    wenn dein Graph alles macht was du willst in eine Komponente stecken und diese nur noch über die Eigenschaften steueren (auch die Zeichendaten).



  • Hallo,
    nein das möchte ich auch nicht. DAs Problem ist einfach nur, dass auch falsch gezeichnet wird, wenn ich die Komponente selber zeichnen lassen möchte. Sieh dir mal das an:

    void __fastcall TGraph::Zeichne()
    {
     Nullpunkt(); //Nullpunktsetzung
     Canvas->LineTo(200,200); //Zeichne VOM Nullpunkt
     SetViewportOrgEx(Canvas->Handle, 0, 0, NULL); //Zurücksetzen
    }
    

    Wen ich jetzt in meiner Button Anweisung das schreib:

    Graph1->Zeichne();
    

    dann wird auch nicht von meinem gewünschten Nullpunkt gezeichnet. Das verstehe ich nicht.

    Warum wird in der OnPaint Methode, das Koordinatenkreuz korrekt gezeichnet, aber sobald ich eine andere Zeichnung mache, wird für mich an einem wahllosen Punkt angefangen, obwohl ich "Nullpunkt" aufgerufen habe. Ein anderes Problem liegt ja eigentlich gar nicht vor. Ich habe nur halt schon so viel ausprobiert, weshalb Methoden wie "Zeichne" oder "Linie" zustande kommen. Später wird das natürlich anders aussehen, doch erstmal will ich halt, dass korekt gezeichnet wird, da sonst nie ein Graph zustande kommt.

    Vielen Dank, VergissEs! Kannst Du oder jemand anderes sagen, warum das Ergebnis von anderen Zeichnungen nicht meinen ERwartungen entsprechen und warum nur in OnPaint richtig gezeichnet wird. Vielleicht habe ich ja uch grundsätzlich etwas nicht verstanden!

    Bis bald
    lg, freakC++

    edit: Ich habe nochmal ein bisschen rumgespielt. ALle ZEichnungen aus DoPaint werden korrekt gezeichnet, doch alle anderen, wie solche in MEthoden wie "Zeichne" werdden "falsch" gezeichnet.



  • Bei der VCL ist der 0-Punkt immer oben links wenn du nun in OnPaint den 0-Punkt
    verschiebst dann hat natürlich die VCL Probleme damit, deshalb wieder
    zurückstellen. Dich zwingt auch keiner den 0-Punkt zu verschieben das ganze
    kann auch wie TH69 in seiner Funktion geschrieben hat erfolgen, also die ganzen Koorinaten selbst berechnen.

    Du weisst schon das OnPaint automatisch immer aufgerufen wird zum Beispiel ein anderes Fenster über deinen Graphen gezogen und dann wieder sichtbar wird.
    Wenn du nun eine Zeichenfunktion Graph1->Zeichne(); manuell ausführst wird diese natürlich nicht im OnPaint berücksichtigt.
    Wenn du schon ein neuzeichnen des Graphen erzwingen willst,dann Bitte mit der schon existierenden Graph1->Repaint();
    UND ALLES IN OnPaint zeichnen lassen!



  • Du darfst einfach NICHT außerhalb des OnPaint-Ereignisses zeichnen - PUNKT!

    So langsam zweifel ich auch daran, ob du wirklich den Sinn einer Komponente verstanden hast.
    Ein Button-Click darf allerhöchstens Invalidate() für die PaintBox aufrufen, und dann muß sich die PaintBox selbständig zeichnen (im OnPaint-Ereignis) - so wie akari, VergissEx und kpeter auch schon vorher geschrieben haben!!!

    Du mußt nur dafür sorgen, daß die entsprechenden Parameter für deine PaintBox-Komponente vorher gesetzt sind (per Eigenschaften oder Methoden, welche intern eine Liste von Koordinaten als private Member verwalten o.ä.).

    Sorry, aber ich verabschiede mich jetzt (für diese Jahr) aus diesem Thread - Guten Rutsch...



  • Hallo Th69,
    vielen Dank für deinen Hinweis. Deine Zweifel sind duchaus angebracht, doch kenne ich den Sinn einer Komponente und wusste nicht, dass ich nicht außerhalb des OnPaint Ereignisses zeichnen darf. Ich meinem Buch stand immer soetwas (es hat sich dann auf das Formular bezogen).

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
     Canvas->LineTo(200,200),
    }
    

    Hier wird auch ohne OnPaint gezeichnet und es hat funktioniert. So habe ich Canvas kennen gelernt. Naja, ich versuche deinen Tipp anzuwenden. Das bedeutet also, dass ich immer das OnPaint Event aufrufen muss, um zu zeichnen?

    Ich wünsche dir auch einen guten Rutsch und bis nächstes Jahr!
    Vielen Dank für deine Hilfe für einen Anfänger

    lg, freakC++



  • freakC++ schrieb:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
     Canvas->LineTo(200,200);
    }
    

    Nach dem Butten Click, schieb mal das Fenster soweit aus dem Bildschirm bis die Linie nicht mehr sichtbar ist und schieb das Fenster wieder auf den Bildschirm dann wirst du festellen das deine gezeichnete Linie nicht mehr da ist.
    Und wiso das so ist.... darft du nun mal dir überlegen 🙂



  • Hallo VergissEs,
    danke auch für deine Antwort. Wie ihr sichlich nun schon mitbekommen habt, bin ich nicht allzu vertaut mit der VCL. Dennoch WILL ich das lernen und bitte darum mir zu heflen, dass hier endlich abzuschließen,.

    Das ich immer innerhalb OnPaint zeichen muss, war mir nicht klar. Daran wird es sicherlich liegen. Natürlich könnte ich alles umrechnen, so wie es TH69 getan hat, doch ich möchte es jetzt so machen, um somit hoffentlich auch einiges zu lernen, damit solche Fragen nicht mehr auftreten. Kannst Du mir mal an einer Methode, die eine Linie nach (200|200) zeichnet, zeigen, wie "ich innerhalb des OnPaint Ereignisses" zeichne. Das gleiche müsste ich dann doch auch mit der Methode "Linie" meiner Komponente TGraph machen? Muss ich da meine Methode "Linie" oder "Zeichne" wie DoPaint OnPaint zuweisen? Könntest DU mir zeigen wie das geht, denn das habe ich nie gelernt und somit würde ich wieder meine Grundlagen erweitern :D.

    Ich möchte halt später Methoden wie DrawGraph oder Ähnliche haben, die wirklich meinen Graphen zeichnen. Wie bringe ich die mit OnPaint in Verbindung, damit richtig gezeichnet wird?

    Bei deinem Beispiel liegt es wohl daran, dass das Fenster nicht neugezeichnet wird. Ja, das ist mir klar, doch wie bringe ich dann das in Verbindung mit OnPaint. Wenn ich das weiß, müsste ja eigentlich alles funktionieren.

    Ich danke dir sehr!
    lg, freakC++



  • Versteh jetzt nicht wo dein Problem ist DrawGraph von DoPaint aus aufzurufen?

    im Header

    private:
    	TColor BackgroundColor;
    	TColor FrameColor;
    	void __fastcall TMyPaintBox::DoPaint(TObject *Sender);
    	void __fastcall DrawBackground();
    	void __fastcall DrawGraph();
    
    __fastcall TMyPaintBox::TMyPaintBox(TComponent* Owner)
    	: TPaintBox(Owner)
    {
    	OnPaint = DoPaint;
    	BackgroundColor = clWhite;
    	FrameColor = clBlack;
    }
    //---------------------------------------------------------------------------
    void __fastcall TMyPaintBox::DrawBackground() {
    
    	Canvas->Brush->Color = BackgroundColor;
    	Canvas->Pen->Color = FrameColor;
    	Canvas->Pen->Style = psSolid;
    
    	Canvas->Rectangle(ClientRect);
    }
    //---------------------------------------------------------------------------
    void __fastcall TMyPaintBox::DrawGraph() {
    
    	Canvas->Pen->Style = psSolid;
    	Canvas->Pen->Color = clBlack;
    
    	//Horizontale Linie
    	Canvas->MoveTo(0, Height/2);
    	Canvas->LineTo(0, -Height/2);
    
    	//Vertikale Line
    	Canvas->MoveTo(Width/2, 0);
    	Canvas->LineTo(-Width/2, 0);
    }
    //---------------------------------------------------------------------------
    void __fastcall TMyPaintBox::DoPaint(TObject *Sender)
    {
    
    	//Hintergrund zeichnen
    	DrawBackground();
    
    	//Festlegen das 0/0 Punkt im zentrum liegt
    	SetViewportOrgEx(Canvas->Handle, Width/2, Height/2, NULL);
    
    	//Graphen zeichnen
    	DrawGraph();
    
    	//0/0 Punkt wieder oben links setzen
    	SetViewportOrgEx(Canvas->Handle, 0, 0, NULL);
    }
    //---------------------------------------------------------------------------
    


  • Hallo,
    vielen Dank für den Code, doch leider haben wir uns missverstanden. Nehmen wir an, dass DrawGraph erstmal nichts anderes macht, als eine Linie zu zeichnen:

    void __fastcall TGraph::DrawGraph()
    {
     Canvas->LineTo(200,200);
    }
    

    Nun soll aber DrawGraph nicht von "DoPaint" aufgerufen werden, da der Strich ja schon zu Beginn gezeichnet werden würde. Der Benutzer soll aber selbst bestimmen, wann diese Methode aufgerufen wird und erst z.B. bei Knopfklick den Graphen zeichnen lassen.

    Ich kann, wie ihr mir gesagt habt, nicht einfach DrawGraph() aufrufen, da sie nicht innerhalb von OnPaint zeichnet. Wenn ich das täte, dann würde mein Problem wieder, nämlich, dass nicht vom Ursprung aus gezeichnet wird (auch wenn ich diesen verschoben habe und dahin "gemoved" bin) erscheinen.

    Wie muss ich nun "DrawGraph" modifizieren, damit sie mit OnPaint in Verbindung steht, beziehungsweise, dass die Linie vom Ursprung aus gezeichnet wird?

    Ich hoffe, dass Du mich verstanden hast. Falls Du nicht mehr zum Antworten kommst, wünsche ich dir und auch allen anderen einen guten Rutsch mit vielen Böllern und Raketen :D.
    Vielen Dank für die Hilfe, die hoffentlich bald nicht mehr notwendig ist.
    lg, freakC++


Anmelden zum Antworten