Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

1

05.07.2010, 22:50

exponentielle Funktionssegmente darstellen

Hallo,

ich arbeite gerade an meinem Soundeditor weiter und habe ein Problem: Ich habe eine Liste (m_fEnvelopeProfile.afPoints[]) mit Werten (soll z.B. als Frequenzverlauf in einer bestimmten Zeit dienen). Die Liste möchte ich jetzt graphisch darstellen, dazu benutze ich ein TPaintBox Steuerelement aus der IDE meines Borland Compilers. Es soll 2 Möglichkeiten geben, wie diese Liste mit Werten behandelt wird:
1. Es wird linear zwischen den Werten interpoliert -> es werden lineare Segmente gezeichnet (funktioniert)
2. Es wird exponentiell zwischen den Werten interpoliert -> es werden exponentielle Segmente gezeichnet (funktioniert noch nicht)
Auf das TPaintBox (PBGraph) Steuerelement kann man mittels einer Canvas Struktur zeichnen. Es ist 433 Pixel breit und 266 Pixel hoch (abzüglich 5 Pixel aufgrund eines Rahmens an jeder Seite).

Ich bin davon ausgegangen, dass man die Gleichung §f(x) = b^{x} § hat und b für das jeweilige Segment so ausrechnen kann: §b = (Wert vom aktullen Listeneintrag / Wert vom vorherigen Listeneintrag)^{1 / (Position des aktuellen Listeneintrags - Position des vorherigen Listeneintrags)} § Wenn man b nun hat kann man die 1. Gleichung benutzen und für x die Position in Pixel einsetzen, für die man den Wert des Graphen in Pixel wissen will. Jedoch scheint irgendwas an meinem Code oder meiner Überlegung falsch zu sein, da die Graphen mal zu steil, mal zu flach verlaufen.

Hier ist meine Paint-Methode:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void __fastcall TFEnvelope::OnPaintGraph(TObject *Sender) 
{ 
    // Rahmen (5 Pixel Abstand von jeder Seite) zeichnen und PaintBox grau färben
    PBGraph->Canvas->Brush->Color = clSilver; 
    PBGraph->Canvas->Rectangle(0, 0, PBGraph->Width, PBGraph->Height); 

    PBGraph->Canvas->Pen->Color = clBlack; 
    PBGraph->Canvas->PenPos = TPoint(4, 5); 
    PBGraph->Canvas->LineTo(PBGraph->Width - 5, 5); 
    PBGraph->Canvas->PenPos = TPoint(4, 5); 
    PBGraph->Canvas->LineTo(4, PBGraph->Height - 5); 
    PBGraph->Canvas->Pen->Color = clWhite; 
    PBGraph->Canvas->PenPos = TPoint(PBGraph->Width - 5, PBGraph->Height - 5); 
    PBGraph->Canvas->LineTo(PBGraph->Width - 5, 5); 
    PBGraph->Canvas->PenPos = TPoint(PBGraph->Width - 5, PBGraph->Height - 5); 
    PBGraph->Canvas->LineTo(4, PBGraph->Height - 5); 

    PBGraph->Canvas->Brush->Color = clBlack; 
    PBGraph->Canvas->Pen->Color = clNavy; 

    int iPosition = 0; // Nummer des aktuellen Elements in der Liste 
    int iLastI = 0; // Position des letzten Listeneintrags (Liste hat 101 Elemente , wobei das nullte Element den Wert nach 0% der Zeit und das hundertste Element den Wert nach 100% der Zeit darstellt)
    float fB; // Variable b der Gleichung
    float fValue = 0.0f, fLastValue; / /normalisierter Wert (so angepasster Wert, dass er von 0 nach x geht und nicht im negativen Bereich ist) des des aktuellen und des vorherigen behandelten Elements der Liste.

    for(int i = 0; i < 101; i++) // alle Elemente durchgehen
    { 
        if((m_EnvelopeProfile.afPoints[i] == -999999.999f) && (i != 0) && (i != 100)) // -999999.999f bedeutet Wert nicht gesetzt (also hier interpolieren) bei der letzten und der 1. Position in der Liste wird in diesem Fall der Standard-Wert m_fDefault verwendet
            continue; 

        if(m_EnvelopeProfile.afPoints[i] == -999999.999f) 
            m_EnvelopeProfile.afPoints[i] = m_fDefault; 

        fLastValue = fValue;
        fValue = -m_EnvelopeProfile.afPoints[i] + m_fMax; // aktuellen Wert normalisieren

        if(m_EnvelopeProfile.bIsLinear) 
        { 
           // [...] lineare Segmente zeichnen, wenn linear interpoliert werden soll
        } 
        else 
        { 
            if(i == 0) 
                PBGraph->Canvas->PenPos = TPoint(5, (int)(((float)(PBGraph->Height - 11)) / (m_fMax - m_fMin) * fValue + 5.0f)); Stift auf Anfangswert setzen (diese Zeile muss man nicht verstehen)
            else 
            { 
                fB = pow(fValue / fLastValue, 1.0f / (i - iLastI)); // b ausrechnen 

                for(int j = PBGraph->Canvas->PenPos.x - 5; j <= (int)(((float)(PBGraph->Width - 5)) / 100.0f * (float)(i)); j++) // für die Anzahl an Pixel zwischen den beiden Elementen ausführen (5 Pixel Rahmen berücksichtigen!), PenPos.x steht an der aktuellen Position
                { 
                    if(fB < 1.0f) // wenn b < 1, dann exponentielle Abnahme, indem der Kehrwert von b verwendet wird und das Ergebnis der Potenzierung in den negativen Bereich gesetzt wird
                        PBGraph->Canvas->Pixels[j][(int)(-pow(1.0f / fB, j - PBGraph->Canvas->PenPos.x + 6)) + PBGraph->Canvas->PenPos.y] = clNavy; // Pixel an Position j (aktuelle Position in Pixel) | b^Anzahl der bearbeiteten Pixel + "Startwert" (PenPos.y, Höhe des letzten Elements der Liste) setzen
                    else 
                        PBGraph->Canvas->Pixels[j][(int)(pow(fB, j - PBGraph->Canvas->PenPos.x + 6)) + PBGraph->Canvas->PenPos.y] = clNavy; 
                    if(j == (int)(((float)(PBGraph->Width - 5)) / 100.0f * (float)(i))) // wenn das letzet Pixel des aktuellen Segments gesetzt wurde, dann Position des Stiftes anpassen
                        if(fB < 1.0f) 
                            PBGraph->Canvas->PenPos = TPoint((int)((float)(PBGraph->Width - 5) / 100.0f * (float)(i)), (int)(-pow(1.0f / fB, j - PBGraph->Canvas->PenPos.x + 6)) + PBGraph->Canvas->PenPos.y); 
                        else 
                            PBGraph->Canvas->PenPos = TPoint((int)((float)(PBGraph->Width - 5) / 100.0f * (float)(i)), (int)(pow(fB, j - PBGraph->Canvas->PenPos.x + 6)) + PBGraph->Canvas->PenPos.y); 
                }  
            } 
        } 

        // [...] an Punkten kleine Quadrate zeichnen

        iPosition++; 
        iLastI = i; 
    } 

   // [...] Achsen beschriften
}


Ich hoffe ihr versteht mein Problem und könnt mir helfen, ich bin fast am verzweifeln, da ich schon mindestens 5 Stunden damit verbracht habe dieses Problem zu lösen.
mfg BurningWave

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

05.07.2010, 23:05

Ich glaube was du suchst nennt sich Logarithmus

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

3

05.07.2010, 23:12

Ich kenne den Logarithmus, aber die Formel, von mir sollte eigentlich stimmen, da ich nicht den Exponent sondern die Basis suche.

the[V]oid

Alter Hase

Beiträge: 775

Wohnort: Aachen

  • Private Nachricht senden

4

05.07.2010, 23:37

Muss zugeben, hab deinen Beitrag jetzt nur schnell überflogen, aber vllt hilft es dir trotzdem..

Also wenn §x \neq 0§ die aktuelle Position in deiner Liste ist, §f(x)§ der Wert vom Listeneintrag an der Stelle §x§, und du §b§ in der Gleichung §f(x)=b^x§ suchst, dann ist dein §b§ durch §f(x)^{1/x}§ gegeben.

Die Formel die du da hattest sah mir irgendwie zu verwurschtelt aus O_o
<< an dieser Stelle ist eine Signatur verstorben >>

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »the[V]oid« (05.07.2010, 23:43)


Mastermind

unregistriert

5

05.07.2010, 23:54

Ich gehe davon aus dass du wirklich Interpolieren willst und keine Regression meinst.

Interpolieren macht ja nur zwischen zwei benachbarten Werten Sinn, das meinst du dann wohl auch mit "Wert vom vorherigen Eintrag" nehme ich an.

Lass uns mal anfangen, dass wir WertvomaktuellenListenEintrag y_i nennen also entsprechend deiner Gleichung:

§b = (\frac{y_i}{y_{i-1}})^{\frac{1}{i-(i-1)}}§

Dadurch haben wir schonmal soviel Klarheit gewonnen, dass der Exponent immer 1 ist, also auch nichts weiter bringt.

Was noch nicht so ganz klar ist, ist wo du hin willst. Ich nehme jedoch an, der Plan bei

§f(x)=b^x§

war, das insbesondere gelten sollte dass

§f(i)=y_i§

Wie man durch Einsetzen leicht herausfinden kann, ist

§f(i) = b^i = (\frac{y_i}{y_{i-1}})^i \neq y_i§

für

§y_{i-1} \neq {y_i}^{i-1}§

also fast immer.

Langer Rede kurzer Sinn:

Zitat

aber die Formel, von mir sollte eigentlich stimmen


Tut sie aber nicht, es sei denn du wolltest estwas ganz anderes erreichen.

Ich würde probieren

§f(i)=ae^i+b=y_i§

zu lösen. Ich weiß nicht ob das besser ist, aber die dunkle erinnerung an Mathe LK suggeriert sowas.

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

6

06.07.2010, 15:19

Vielleicht hilft dir das Paper ja. Auf Seite 4 findest du einen Abschnitt zum Thema Exponential interpolation.
@D13_Dreinig

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

7

06.07.2010, 22:02

Danke schon mal für dien Antworten, aber mein Hauptproblem ist eigentlich, wie ich, wenn ich 2 Punkte (in Form von einem Wert zwischen 0 und x als y-Koordinate und einer Position von 0 bis 100 als x-Kordinate) habe, den exponentiellen Verlauf darstellen kann.

Ich habe mir schon 2 Möglichkeiten ausgedacht (wobei ich versucht habe die 1. zu realisieren, was aber nicht klappt, da das ausgerechnete b irgendwie nicht stimmt):
1. Exponentielle Regression, also aus den 2 Punkten (P und Q) b ausrechnen (mit §b = (Q_{y} / P_{y})^{1 / (Q_{x} - P_{x})} §) und dann je nach Pixel den Wert mit §f(x) = b^{x} § (x entspricht aktuellem Pixel) ausrechnen.
2. Exponentiell zwischen den Werten interpolieren und dadurch irgendwie auf den Wert an der Position x schließen.

Meine Bitte wäre jetzt, dass ihr versucht den Denkfehler von mir oder den Fehler in meinem Code zu finden. Ich werde mittlerweile mal eure Vorschläge ausprobieren.

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

8

07.07.2010, 19:18

Es gibt Fortschrittte :D

Ich habe die innere Schleife jetzt durch das ersetzt:

C-/C++-Quelltext

1
2
3
4
5
6
for(int j = PBGraph->Canvas->PenPos.x - 5; j <= (int)(((float)(PBGraph->Width - 5)) / 100.0f * (float)(i)); j++) 
{ 
    PBGraph->Canvas->Pixels[j][(int)((PBGraph->Height - 11) / (m_fMax - m_fMin) * (fLastValue * pow((fValue + 1.0f) / (fLastValue + 1.0f), (float)(j - PBGraph->Canvas->PenPos.x - 5) / (((float)(PBGraph->Width -    5)) / 100.0f * (float)(i)))) + 5.0f)] = clNavy; 
    if(j == (int)(((float)(PBGraph->Width - 5)) / 100.0f * (float)(i))) 
        PBGraph->Canvas->PenPos = TPoint(j, (int)((PBGraph->Height - 11) / (m_fMax - m_fMin) * (fLastValue * pow((fValue + 1.0f) / (fLastValue + 1.0f), (float)(j - PBGraph->Canvas->PenPos.x - 5) / (((float)(PBGraph->Width - 5)) / 100.0f * (float)(i)))) + 5.0f)); 
}


Es Spiegelt die Formel zur exponentiellen Interpolation
§y(x) = y_{1} * (y_{2} / y_{1})^{x} §
wieder.

Einzelne exponentielle Segmente werden auch schon angezeigt, jedoch ist alles ein bisschen verschoben (siehe Graphik (2. Graphik: lineare Interpolation (funktioniert)))). Hat jemand eine Idee woran das liegen könnte?
»BurningWave« hat folgende Bilder angehängt:
  • 1.jpg
  • 2.jpg

Mastermind

unregistriert

9

07.07.2010, 21:39

Wie du bestimmt schon erkannt hast, ist y(x) = y1 für x=0 und y2 für x=1. Das hab ich als Ansatz in meinem oberen Beitrag natürlich versemmelt, sorry dafür.

Ich beschrenke mich also auf den Teil zwischen einem Wertepaar (startpixel, endpixel) rechts und links davon kommt natürlich mehr zeichnung ich nehme an dass macht dein i?!

Was du jetzt eigentlich nur machen musst ist den Bereich diskretisieren.

Quellcode

1
2
3
4
5
6
7
8
for (j = startpixel j<=endpixel; j++)
{

    float x = float(j-startpixel)/float(endpixel-startpixel); //für j = startpixel ist das 0 und für j = endpixel 1, so solle es ja auch sein.
    float y = y(x); //wie in deiner formel

    zeichne pixel an der stelle j, floor(y)
}


Was du machst sieht viel zu kompliziert aus, ich mag gar nicht versuchen es zu durchschauen.

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

10

08.07.2010, 20:18

@Mastermind, so mache ich das auch, nur funktioniert es aus irgendwelchen Gründen trotzdem nicht richtig. Die Canvas Struktur kann auch Bézierkurven zeichnen, kann man damit einfach 2 Punkte exponentiell miteinander verbinden?

Werbeanzeige