Rysowanie wykresów i pobeiranie położenia zaznaczonych punkt

0

Witam,

Musiałbym mieć program, który ułatwiłby mi obróbkę danych w trybie graficznym.

Mam dane w pliku tekstowym, po nagłówku w każdej linijce w pliku jest podana pewna wartość (która bedzie na osi Y). Jest też podana odległość między tymi punktami (oś X).
Przykład:

SPACING_UM: 1.905388 (odległość między punktami czyli oś X)

NUMBER_OF_POINTS: 8000 (Liczba wszystkich punktów)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-comfficeffice" />

PROFILE_UM: (wartości na osi Y)

3.682900

4.215500

4.034400

3.256800

itd. ...

Chodzi o wyświetlenie tych danych na wykresie, ale ważniejsza jest możliwość zaznaczania myszą interesującego mnie obszaru (zaznaczony na czerwono w załączniku) i zapisanie go w nowym pliku.

PS Myślałem nad zapisaniem pliku TXT jako INI i wtedy może łatwiej by było go odczytać.
Lecz problem tkwi w tym, że trzeba zapisac do pliku te punkty pokazane na obrazku poniżej, Myślałem nad komponentem TCanvas ale chyba nie jestem w stanie tak zrobic żeby mi pokazało położenie na osi X i Y zaznaczonych punktów.

user image

0

REF

Pomożę ktoś. To na prawde pilne !!!

0

Rysowanie via GDI jest dziecinnie proste. Połóż na formatkę TPainBox'a, TCanvas + F1 i jazda. Niezależnie od tego co będziesz malował, musisz mieć zdeklarowane struktury na dane w programie, z których to struktur będziesz dopiero wybierał pdzbiór danych (przez zaznaczenie). Samo malowanie jest proste i przyjeme. Do pełni szczęścia będziesz potrzebował jeszcze dwóch funkcji do przeliczania między współrzędnymi pixelowymi i rzeczywistymi. Jeśli chcesz mieć możliwość wyświetlania obrazu gdzie skala x jest różna od skali y, to musisz mieć dwie skale zdeklarowane. I w zasadzie chyba tyle. Jakby Ci się chciało, to możesz malować na canvasie bitmapy w pamięci, a potem kopiować bitmape na paintbox'a.

b

0

No ok ale jak będzie wyglądać pobieranie danych z takiego pliku ?

ewentualnie można zrobić tak że w jednym polu podac odległości pomiędzy punktami a w drugim wszytskie punkty.

Jest też sprawa z tym że trzeba ję będzie ptrzeskalowac bo mogą się nie mieścić na ekran tzn rozdzielczość przykładowo 1024x768 a do wyswietlenia punktów 8000

0

Wszystko to jest proste jak budowa cepa.
Musisz mieć w programie tablicę, na przechowywanie wczytanych punktów. Najprościej przy wczytywaniu zapisywać sobie od razu X i Y. Przy czym X będzie obliczony jako suma SPACING_UM. Powiedzmy:

TPunkt = record
X, Y : Real
end;

Punkty : array of TPunkt;

Przy wczytywaniu od razu ustaw wielkość tablicy na tyle na ile potrzeba (SetLength).

Po drugie musisz móc jakoś przeliczać współrzędne: ekranowe <-> rzeczywiste. Zakładając że malujemy po wrzuconym na formatkę TPainBox.Canvas, minimalny zestaw parametrów jakie potrzebujesz to:
kX - skala w kierunku X
kY - skala w kierunku Y
Xmin, Ymin - współrzędne rzeczywiste lewego dolnego rogu Canvas'a (co odpowiada współrzędnym ekranowym (0, P.Height).
Musisz przyjąc że np skala to ile pixeli wejdzie na jednostkę rzeczywistą (lub odwrotnie). W każdym razie muszisz wypłodzić wzór na x = FX(X) i y = FY(Y). Pamiętaj że współrzędne ekranowe liczy się tak:

|0 x
-------------->
0 |
|
|
|
y/

Jak już masz takie wzory, to w zdarzeniu OnPaint naszego płótna malujesz sobie punkty, lub linie łączące punkty...

P.Canvas.MoveTo(FX(Punkty[0].X), FY(Punkty[0].X));
For i := 1 to High(Punkty)
P.Canvas.LineTo(FX(Punkty[i].X), FY(Punkty[i].X));

Dla bajeru namaluj sobie jeszcze siatkę, osie, i skale. Inne bajery to powiększanie scrollem, przesuwanie 3 przyciskiem...

No i mamy wykres, a teraz zaznaczanie. Tak profesionalnie, to można to zrobić stosując Pen.Mode = pmNotXor, ale nie chce mi się pisać. Od biedy, w OnMouseDown zapisz sobie współrzędne klikniętego punktu nr1 a potem nr2 (musisz zastosować funkcje odwrotne).

A potem export, punktów łapiących się w podanym przedziale. Ja bym zapisał to pliku o takim samym formacie jak źródłowy - masz wówczas wczytywanie z bani.

To wszystko jest naprawde proste, o tutaj jest przykładowy rezultat co można zdziałać na Canvasie:

http://ge0dezja.republika.pl/delphi/perioder.html

Jak Ci się nie chce/nie dajesz rade, to przelej stówe na moje konto to Ci to napisze, i dam razem z kodem - sporo się nauczysz.

b

0

To co mi napisałeś to czarna maga dla mnie niewiele zrozumiałem więc jeżeli mógłbyś wkleić jakieś kody to byłbym wdzięczny,
nie rozumiem np. tego dlaczego po zdeklarowaniu zmiennych robimy jeszcze tablice

Punkty : array of TPunkt;

Jeśli chodzi o to na czym malujemy to tak na PaintBoxie

Jak już masz takie wzory, to w zdarzeniu OnPaint naszego płótna malujesz sobie punkty, lub linie łączące punkty...

P.Canvas.MoveTo(FX(Punkty[0].X), FY(Punkty[0].X));
For i := 1 to High(Punkty)
P.Canvas.LineTo(FX(Punkty[i].X), FY(Punkty[i].X));

nie lepiej użyć

Canvas.Pixels[x,y:integer]

I jeszcze jedno nie mam pomocy dla tego komponentu w delphi

i myślałem nad czymś takiem ale mi błędy wywala (można zrobic tak ze wartosci sie do pól wrzuca ;])

var
x, y : real;
p : integer;

begin
x := strtofloat(x.Text);
for p := 0 to 10000 do
begin
y := strtofloat(Memo1.Lines.Strings[p]);
Canvas.Pixels[x,y];
x := x+strtofloat(x.Text);
end;

0
blackshadow napisał(a)

To co mi napisałeś to czarna maga dla mnie niewiele zrozumiałem więc jeżeli mógłbyś wkleić jakieś kody to byłbym wdzięczny,

To tak jak pisałem, stówa i kodzik ładnie sformatowany ;] Za dużo klepania żebym się za to z wolontariatu brał.

blackshadow napisał(a)

nie rozumiem np. tego dlaczego po zdeklarowaniu zmiennych robimy jeszcze tablice

Punkty : array of TPunkt;

Punkty to tablica rekordów typu TPunkt. Element tablicy to punkt wykresu.

blackshadow napisał(a)

nie lepiej użyć

Canvas.Pixels[x,y:integer]

Nie. Po pierwsze ta metoda jest bardzo wolna. Po drugie, nie ma się co pieprzyć z punktami - lepiej pociągnąć kreske, no chyba że Ci bardzo zależy. Tak czy siak, jako x i y musisz wziąć wynik przeliczenia współrzędnych rzeczywistych.

blackshadow napisał(a)

I jeszcze jedno nie mam pomocy dla tego komponentu w delphi

Do czego nie masz ? Do TCanvas czy to TPaintBox ? Ej ej weźno nie cygań, bo Cię wezmę za lenia i się wiecej nie odezwę. Cała moja wiedza na temat malowania to Help do Delphi (no i pare książek). Tak czy siak sam help wystarczy.

blackshadow napisał(a)

i myślałem nad czymś takiem ale mi błędy wywala (można zrobic tak ze wartosci sie do pól wrzuca ;])

var
x, y : real;
p : integer;

begin
x := strtofloat(x.Text);
for p := 0 to 10000 do
begin
y := strtofloat(Memo1.Lines.Strings[p]);
Canvas.Pixels[x,y];
x := x+strtofloat(x.Text);
end;

Oj kolego, marnie to widzę. Ale niech stracę, za 80 PLN, zrobie Ci to co chcesz, kod będzie ładnie sformatowany i jeszcze z komentarzami ;]
Cóż mogę rzec, kod ma błędy w każdej linii. Nie wiem czy jest sens Ci cokolwiek tłumaczyć. Brakuje Ci podstaw, więc próżno tłumaczyć problem od strony merytorycznej. Może się ktoś zlituje, chyba że mnie jakoś ujmiesz.

b

0

Myślę że zrezygnuje ;]

jeszcze jedno mam juz tablice zdeklarowaną "lenght' i mi zostało tylko wczytanie punktów i ich wyświetlenie myślałem nad

  Punkty.X[0] := strtofloat(Edit2.Text); 

ale jest błąd nie wiem dlaczego (żeby nie było tylko pierwszy X jest wczytywany z pola edit)

0

Punkty[0].X := ...

Mam nadzieje że wcześniej alokowałeś pamięć na punkty (SetLength(Punkty, 8000)), lub użyłeś tablicy o stałym rozmiarze ( Punkty : array [0..8000] of TPunkt).

0

próbuje to na monitor rzucic ale delphi nie zna komend FX i FY zresztą ja też podejrzewam tylko że jet to wyrzucenie znaków po przecinku

0

REF

[DCC Error] Unit1.pas(52): E2003 Undeclared identifier: 'FY'

[DCC Error] Unit1.pas(52): E2003 Undeclared identifier: 'FX'

[DCC Error] Unit1.pas(54): E2029 'DO' expected but identifier 'paintbox' found

takie błędy

0

Motasz i motasz. FX i FY to sobie musisz sam napisać. To muszą być funkcje które Ci przeliczają współrzędne ekranowe na rzeczywiste i odwrotnie. Nazwa jest dowolna.

function E2RX(x : Integer) : Real;
function E2RY(y : Integer) : Real;

function R2EX(X : Real) : Integer;
function R2EY(Y : Real) : Integer;

Musisz sobie te funkcje napisać. W obliczeniach musisz użyć, tych skal kX i kY, Xmin, Ymin, p.Height. Te całe obliczenia zajmują 1 linijkę.

Ostatecznie za 50 PLN mogę Ci to napisać : )
b

0

No i co poddałeś się ? Może hint:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TPunkt = record
    X, Y : Real;
  end;

  Tf = class(TForm)
    p: TPaintBox;
    procedure pPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    // ile jest pixeli na 1 metr
    kX, kY : Real;
    // współrzędne rzeczywiste lewego dolnego narożnika
    Xmin, Ymin : Real;
    // dane, muszą być posortowane
    Punkty : array of TPunkt;
    function R2EY(Y : Real) : Integer;
    function R2EX(X : Real) : Integer;
  public
    { Public declarations }
  end;

var
  f: Tf;

implementation

{$R *.dfm}

procedure Tf.FormCreate(Sender: TObject);
begin
  SetLength(Punkty, 4);
  Punkty[0].X := 10;
  Punkty[0].Y := 10;
  Punkty[1].X := 20;
  Punkty[1].Y := 5;
  Punkty[2].X := 7;
  Punkty[2].Y := 31;
  Punkty[3].X := 17;
  Punkty[3].Y := 18;
  Xmin := 0;
  Ymin := 0;
  kX := 10;
  kY := 5;
end;

procedure Tf.pPaint(Sender: TObject);
var i : Integer;
begin
  With (Sender as TPaintBox).Canvas do
    Begin
      FillRect(p.ClientRect);
      If Length(Punkty) > 1 then
        Begin
          MoveTo(R2EX(Punkty[0].X), R2EY(Punkty[0].Y));
          For i := 1 to High(Punkty) do
            Begin
              LineTo(R2EX(Punkty[i].X), R2EY(Punkty[i].Y));
            End;
        End;
    End;
end;

function Tf.R2EX(X: Real): Integer;
begin
  Result := Round((X - Xmin) * kX);
end;

function Tf.R2EY(Y: Real): Integer;
begin
  Result := p.Height - Round((Y - Ymin) * kY);
end;

end.
0

chory jestem i miałem trochę roboty w szkole i Wielkieeee THXX za kodzik

Zacznę teraz pracować ;]

0

REF

Ja zrobiłem to w formie przycisku po jego naciśnięciu pobierają się dane i wywalają na paint boxa
i ERR

[Error] Unit1.pas(61): Record, object or class type required

[Error] Unit1.pas(61): 'END' expected but ')' found

[Error] Unit1.pas(62): Missing operator or semicolon

nie wiem o co chodiz poniżej cały source

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  Tf = class(TForm)
    p: TPaintBox;
    Memo1: TMemo;
    pow: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
      { Private declarations }
    // ile jest pixeli na 1 metr
    kX, kY : Real;
    // współrzędne rzeczywiste lewego dolnego narożnika
    Xmin, Ymin : Real;
    // dane, muszą być posortowane
    function R2EY(Y : Real) : Integer;
    function R2EX(X : Real) : Integer;
  public
    { Public declarations }
  end;

TPunkt = record
X, Y : Real
end;

var
  f: Tf;

implementation

{$R *.dfm}

procedure Tf.Button1Click(Sender: TObject);
var
   Punkty : array of TPunkt;
   i, p : integer;
begin

      p := strtoint(pow.text);
      SetLength(Punkty, p);
      for i := 0 to High(punkty) do
      begin
      Punkty[i].Y := strtofloat(Memo1.Lines[i]);
      end;
      Punkty[0].X := strtofloat(Edit2.Text);
      for i := 1 to High(punkty) do
        begin
          Punkty[i].X := Punkty[i].X + strtofloat(Edit2.Text);
        end;

      With (Sender as TPaintBox).Canvas do
    Begin
           FillRect(p.ClientRect);
      If Length(Punkty) > 1 then
        Begin
          MoveTo(R2EX(Punkty[0].X), R2EY(Punkty[0].Y));
          For i := 1 to High(Punkty) do
            Begin
              LineTo(R2EX(Punkty[i].X), R2EY(Punkty[i].Y));
            End;
        End;
    End;



function Tf.R2EX(X: Real): Integer;
begin
  Result := Round((X - Xmin) * kX);
end;

function Tf.R2EY(Y: Real): Integer;
begin
  Result := p.Height - Round((Y - Ymin) * kY);
end;


end.
0

Nie chce mi sie szukać. Może nazwa p się gryzie z nazwą paintbox'a. Trzeba było wkleić linie z błędem a nie cały kod.

0

linia która sie gryzie to FillRect(p.ClientRect);

0

REF

dałem rade ale pokazuje się bład po kliknięciu buttona od pobrania danych i ich przedstawienia

invalid class typecast

a jeżeli zrobię ilość powtórzeń jako większą niż jest punktów to mi pokazuje ze "jest nie właściwą wartością dla float jeżeli to coś pomoże

0

Pomoże ktoś ?
zastawiłem coś ala pułapki ;] i jest to
coś w stylu

With (Sender as TPaintBox).Canvas do
Begin
FillRect(paint.ClientRect);

tych linii
dodam zę nie jest to w on paint może to coś pomoże

++edit 1 ++
jest tak zamieniłem linie with sender as tpaintbox na
with paint.Canvas do
ale efekt jest taki ze po naciśnięciu buttona tworzy sie ten komponent na formatce ale nie wyświetla się nic na nim

++edit 2 ++
próbował opcji że maluje ale kolor lini jest identyczny co tło ale po dodaniu lini przed rysowaniem
Canvas.Pen.Color:=clRed
jest to samo

1 użytkowników online, w tym zalogowanych: 0, gości: 1