Jak odwrócić analogowy zegar o 180*

0

Witam,

w jaki sposób obrócić zegar o 180* wg. poniższego kodu? Zegar musi również chodzić prawidłowo względem obróconej pozycji.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

TClockOptions = packed record
    HourWidth: Integer;
    HourColor: TColor;
    HourStyle: TPenStyle;
    MinuteWidth: Integer;
    MinuteColor: TColor;
    MinuteStyle: TPenStyle;
    SecondWidth: Integer;
    SecondColor: TColor;
    SecondStyle: TPenStyle;
 end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure DrawAnalogClock(T: TDateTime; Canvas: TCanvas; R, Left, Top: Integer; Options: TClockOptions);
var
  h, m, s, x, y: Integer;
begin
  h := StrToInt(FormatDateTime('h', T));
  m := StrToInt(FormatDateTime('n', T));
  s := StrToInt(FormatDateTime('s', T));
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Left + R);
  y := Round(sin(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Top + R);
  Canvas.Pen.Width := Options.HourWidth;
  Canvas.Pen.Color := Options.HourColor;
  Canvas.Pen.Style := Options.HourStyle;
  Canvas.LineTo(x, y);
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  Canvas.Pen.Width := Options.MinuteWidth;
  Canvas.Pen.Color := Options.MinuteColor;
  Canvas.Pen.Style := Options.MinuteStyle;
  Canvas.LineTo(x, y);
  Canvas.MoveTo(Left + R, Top + R);
  x := Round(cos(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  Canvas.Pen.Width := Options.SecondWidth;
  Canvas.Pen.Color := Options.SecondColor;
  Canvas.Pen.Style := Options.SecondStyle;
  Canvas.LineTo(x, y);
end;

procedure DrawClockShield(Canvas: TCanvas; R, Left, Top, Width: Integer; Color: TColor);
var
  x1, y1, x2, y2, i: Integer;
begin
  Canvas.Pen.Width := Width;
  Canvas.Pen.Color := Color;
  i := 0;
  while i < 360 do
  begin
    x1 := Round(cos(PI * i / 180) * R + Left + R);
    y1 := Round(sin(PI * i / 180) * R + Top + R);
    x2 := Round(cos(PI * i / 180) * (R - (R / 100 * 7)) + Left + R);
    y2 := Round(sin(PI * i / 180) * (R - (R / 100 * 7)) + Top + R);
    Canvas.MoveTo(x1, y1);
    Canvas.LineTo(x2, y2);
    i := i + 30;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Options: TClockOptions; // Deklarujemy zmienną z opcjami zegara
begin
  Repaint; // Czyścimy canvas formy
  Options.HourWidth := 2;
  Options.HourColor := clBlack;
  Options.HourStyle := psSolid;
  Options.SecondWidth := 1;
  Options.SecondColor := clRed;
  Options.SecondStyle := psSolid;
  Options.MinuteWidth := 2;
  Options.MinuteColor := clBlack;
  Options.MinuteStyle := psSolid;
  DrawClockShield(Canvas, 100, 20, 20, 4, clBlack);
  // Rysujemy tarczę zegarową na płótnie formy kolorem czarnym i grubości 4 pikseli. Promień 100 pikseli, pozycja x i y 20 pikseli.

  DrawAnalogClock(Time, Canvas, 100, 20, 20, Options);
  // Rysujemy zegar z aktualnym czasem na płótnie formy. Promień 100 pikseli, pozycja x i y 20 pikseli.

  Caption := FormatDateTime('h:nn:ss', Time); // Wyświetlenie czasu w tytule formy

end;

end.

I drugie pytanie, jak zrobić, aby nad każdą godziną była cyfra z godziną? Canvas.TextOut... Dopiero się uczę rysować na canvasie, więc proszę o pomoc.

Czy jest jeszcze jakiś prostszy sposób na rysowanie zegara analogowego?

1

Przede wszystkim wydziel sobie kod odpowiedzialny za rysowanie linii (wskazówki) - wzór jak widzę znasz (z Sin i Cos); Dzięki temu nie będziesz musiał powtarzać tego samego kodu, tak jak to teraz masz w procedurze DrawAnalogClock; Po drugie, do konwersji stopni na radiany i odwrotnie są funkcje DegToRad i RadToDeg - nie musisz ręcznie tego wyliczać;

A teraz główne pytanie:

w jaki sposób obrócić zegar o 180* wg. poniższego kodu?

Chcesz cały zegar obrócić o 180 stopni? Najprostszym sposobem jest namalowanie całego zegara na pomocniczej bitmapie (trzymanej w pamięci), następnie obrócenie tej grafiki o zadany kąt, na koniec namalowanie wynikowej grafiki na jakimś płótnie (np. formularza czy komponencie TPaintBox);

Samo odwrócenie bitmapy do góry nogami to odpowiednie pozamienianie pikseli miejscami; O 180 stopni to zamiana miejscami wierszy, a następnie odwrócenie kolejności pikseli w każdym wierszu (flip vertical + flip horizontal); Samą rotację o kąt 180 stopni można wykonać bez konieczności używania dodatkowego obiektu bitmapy, do której piksele miały by być przepisywane; Przydadzą się właściwości Width i Height do określenia iteracji pętli, a także ScanLine, aby uzyskać dostęp do pamięci obrazu (zwraca wskaźnik na pierwszy bajt pierwszego piksela zadanego wiersza);

Czyli podsumowując:

  • utwórz pomocniczą bitmapę o zadanych wymiarach,
  • namaluj na niej zegar (posłuż się płótnem bitmapy zamiast płótnem formularza),
  • zamień miejscami wiersze i piksele tej bitmapy,
  • gotową bitmapę namaluj na płótnie okna;
    Możesz też skorzystać z jakiegoś gotowego algorytmu rotacji; W WinAPI jest funkcja SetWorldTransform, której użycie jest dość proste (umożliwia rotację o dowolny kąt, nie tylko co 90 stopni); Przykład jej wykorzystania znajdziesz tutaj;

I drugie pytanie, jak zrobić, aby nad każdą godziną była cyfra z godziną? Canvas.TextOut...

Określ współrzędne cyfry/liczby za pomocą zadanego promienia (jeśli liczby mają się układać w okrąg), używając tego samego wzoru co do obliczeń współrzędnych dla wskazówek; Te współrzędne wykorzystaj w pierwszych dwóch argumentach metody TextOut - w trzecim podaj tekst do namalowania (czyli cyfrę lub liczbę); Ustawienia dla malowanego tekstu zmienia się we właciwości Canvas.Font.

0

Dodaj 6h do małej wskazówki, i 30m do dużej.

0

Hej,

dzięki za opdowiedzi, jutro będę się tym bawił, i popróbuję zamienić kod, aby rysował na bitmapie, następnie spróbuje ją odwrócić.

@furious programming Dlaczego usunąłeś kod z obrotem bitmapy? Nie zdążyłem skorzystać

0

@furious programming Dlaczego usunąłeś kod z obrotem bitmapy? Nie zdążyłem skorzystać

Bo był do bani... O ile faktycznie fajnie działał to tylko dla obrazów, których liczba wierszy była parzysta; A to bardzo niedobrze, bo taki kod jest praktycznie bezużyteczny; Jeśli chcesz to podam Ci kod, który działa zawsze i dla każdej bitmapy (bo używa funkcji SetWorldTransform).

0

Chętnie popatrzę na kod, który zawsze działa, więc możesz go tu wkleić.

Tak na marginesie, piszesz kod z głowy? Np. ten wcześniejszy, lub ten o którym piszesz, że zawsze działa. Chodzi mi o to, czy nie korzystasz z fragmentów kodu, które wcześniej miałeś i sklejasz wszystko w całość. Chyba się ze mną zgodzisz, że wszystko co miało być kiedyś napisane, zostało już napisane, więc teraz większość programistów operuje na gotowych kodach, a jedynie wprowadzając w nich modyfikacje, większe lub mniejsze, ale jednak zawsze zaczynają od czegoś - mowa tu o bardziej skomplikowanych funkcjach.

Jeszcze jak byś mógł podpowiedzieć jak przeinaczyć mój kod, aby zegar rysowany był na bitmapie a nie formie, byłbym wdzięczny. Bez znajomości matmy i tych wzorów, nie dałoby rady napisać tego samemu

0

Chętnie popatrzę na kod, który zawsze działa, więc możesz go tu wkleić.

W takim razie poniżej podaję kod na obrót bitmapy o 180° - korzysta z funkcji SetWorldTransform:

uses
  {..} Windows, Math;

procedure RotateBitmap180(ABitmap: TBitmap);
var
  bmpBuff: TBitmap;
  txfTag: tagXFORM;
  sngSin, sngCos: Single;
begin
  sngSin := Sin(DegToRad(180));
  sngCos := Cos(DegToRad(180));

  txfTag.eM11 :=  sngCos;
  txfTag.eM12 :=  sngSin;
  txfTag.eM21 := -sngSin;
  txfTag.eM22 :=  sngCos;
  txfTag.eDx  := (ABitmap.Width - ABitmap.Width * sngCos + ABitmap.Height * sngSin) / 2;
  txfTag.eDy  := (ABitmap.Height - ABitmap.Width * sngSin - ABitmap.Height * sngCos) / 2;

  bmpBuff := TBitmap.Create();
  try
    bmpBuff.Width  := Round(ABitmap.Width * Abs(sngCos) + ABitmap.Height * Abs(sngSin));
    bmpBuff.Height := Round(ABitmap.Width * Abs(sngSin) + ABitmap.Height * Abs(sngCos));

    SetGraphicsMode(bmpBuff.Canvas.Handle, GM_ADVANCED);
    SetWorldTransform(bmpBuff.Canvas.Handle, txfTag);
    BitBlt(bmpBuff.Canvas.Handle, 0, 0, ABitmap.Width, ABitmap.Height, ABitmap.Canvas.Handle, 0, 0, SRCCOPY);

    ABitmap.Assign(bmpBuff);
  finally
    bmpBuff.Free();
  end;
end;

Nie wiem czy działa szybciej niż moja ręczna implementacja, ale przynajmniej zawsze działa prawidłowo :]

Tak na marginesie, piszesz kod z głowy? Np. ten wcześniejszy, lub ten o którym piszesz, że zawsze działa. Chodzi mi o to, czy nie korzystasz z fragmentów kodu, które wcześniej miałeś i sklejasz wszystko w całość.

Wszystko zależy od tego co chcę zrobić (o jaki algorytm chodzi);

Lubię wymyślać różne rzeczy, więc prawie zawsze najpierw siadam i samemu rozpracowuję temat, drążąc go i testując na różne sposoby; Jeśli nie wychodzi i brakuje już pomysłów to wtedy szukam w sieci informacji o cegiełkach, z których algorytm się składa; Ale to chodzi o jakieś ogóle rzeczy, które można zrobić używając gołego języka; Jeśli muszę skorzystać z funkcji Win32 API (lub po prostu z cudzych bibliotek) to wtedy dokumentacja jest obowiązkiem; Jeśli zagadnienie jest trudne lub bardzo trudne to najpierw pasuje się co nieco o nim dowiedzieć (teorii), ale i tak najpierw próbuję sam, a dopiero później szukam przydatnych informacji (np. przykładowych fragmentów kodu lub gotowych kodów); A jak używam gotowców to i tak chwilę się nimi bawię i przerabiam, aby zrozumieć co wykonują i jak dokładnie działają;

Ten kod który wcześniej podałem napisałem z głowy, bo jest bardzo prosty;

Chyba się ze mną zgodzisz, że wszystko co miało być kiedyś napisane, zostało już napisane, więc teraz większość programistów operuje na gotowych kodach, a jedynie wprowadzając w nich modyfikacje, większe lub mniejsze, ale jednak zawsze zaczynają od czegoś - mowa tu o bardziej skomplikowanych funkcjach.

Większość mnie nie interesuje - jak piszę kod to zawsze chcę go sam napisać i jak najlepiej zrozumieć; IMO większość potrzebuje po prostu danego rozwiązania, bez względu na to czy sami go napiszą, czy przekopiują ze Stack Overflow; Ja tak nie działam - lubię się zagłębiać w temat; Czasem pół dnia spędzam nad jedną funkcją, bawiąc się nią i przerabiając - to pozwala dogłębnie zrozumieć dane zagadnienie; Jest to zabawa w stylu *"a co jeśli ?" - polecam wszystkim;

Jeszcze jak byś mógł podpowiedzieć jak przeinaczyć mój kod, aby zegar rysowany był na bitmapie a nie formie, byłbym wdzięczny.

Wystarczy utworzyć obiekt klasy TBitmap - nic trudnego:

var
  bmpBuffer: TBitmap;
begin
  bmpBuffer := TBitmap.Create();
  bmpBuffer.PixelFormat := pf24bit;
  bmpBuffer.SetSize(200, 200);

Tak utworzona bitmapa posiada rozmiar 200x200 pikseli oraz zawiera 24-bitową głębię kolorów; I teraz referencję do płótna tej bitmapy wystarczy podać w parametrach metod DrawAnalogClock i DrawClockShield, czyli wpisując bmpBuffer.Canvas zamiast Canvas (lub Self.Canvas/Form1.Canvas); Na koniec należy namalować bitmapę na płótnie formularza (ew. uprzednio rotując ją o 180°):

Form1.Canvas.Draw(0, 0, bmpBuffer);

Grafika zostanie namalowana w lewym górnym rogu powierzchni roboczej formularza; Oczywiście ustal sobie własny rozmiar bitmapy pomocniczej (taki, jaki rozmiar ma mieć grafika zegara), a także grafikę tą namaluj w ustalonych przez siebie współrzędnych; Pamiętaj, że współrzędne w oknach liczone są od lewego górnego rogu, który ma koordynaty 0,0.

0

Po zaimplementowaniu kodu wygląda to następująco:

unit uClock;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Math;

type
  TfrmAnalogClock = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

TClockOptions = packed record
    HourWidth: Integer;
    HourColor: TColor;
    HourStyle: TPenStyle;
    MinuteWidth: Integer;
    MinuteColor: TColor;
    MinuteStyle: TPenStyle;
    SecondWidth: Integer;
    SecondColor: TColor;
    SecondStyle: TPenStyle;
 end;

var
  frmAnalogClock: TfrmAnalogClock;
  bmpBuffer: TBitmap;

implementation

{$R *.dfm}

procedure DrawAnalogClock(T: TDateTime; bmpBuffer: TBitmap; R, Left, Top: Integer; Options: TClockOptions);
var
  h, m, s, x, y: Integer;
begin
  h := StrToInt(FormatDateTime('h', T));
  m := StrToInt(FormatDateTime('n', T));
  s := StrToInt(FormatDateTime('s', T));
  bmpBuffer.Canvas.MoveTo(Left + R, Top + R);

  x := Round(cos(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Left + R);
  y := Round(sin(PI * (30 * h + (m / 2) - 90) / 180) * (R - (R / 100 * 30)) + Top + R);
  bmpBuffer.Canvas.Pen.Width := Options.HourWidth;
  bmpBuffer.Canvas.Pen.Color := Options.HourColor;
  bmpBuffer.Canvas.Pen.Style := Options.HourStyle;
  bmpBuffer.Canvas.LineTo(x, y);
  bmpBuffer.Canvas.MoveTo(Left + R, Top + R);

  x := Round(cos(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * m - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  bmpBuffer.Canvas.Pen.Width := Options.MinuteWidth;
  bmpBuffer.Canvas.Pen.Color := Options.MinuteColor;
  bmpBuffer.Canvas.Pen.Style := Options.MinuteStyle;
  bmpBuffer.Canvas.LineTo(x, y);
  bmpBuffer.Canvas.MoveTo(Left + R, Top + R);

  x := Round(cos(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Left + R);
  y := Round(sin(PI * (6 * s - 90) / 180) * (R - (R / 100 * 10)) + Top + R);
  bmpBuffer.Canvas.Pen.Width := Options.SecondWidth;
  bmpBuffer.Canvas.Pen.Color := Options.SecondColor;
  bmpBuffer.Canvas.Pen.Style := Options.SecondStyle;
  bmpBuffer.Canvas.LineTo(x, y);
end;

procedure DrawClockShield(bmpBuffer: TBitmap; R, Left, Top, Width: Integer; Color: TColor);
var
  x1, y1, x2, y2, i: Integer;

begin
  bmpBuffer.Canvas.Pen.Width := Width;
  bmpBuffer.Canvas.Pen.Color := Color;
  i := 0;
  while i < 360 do
  begin
    x1 := Round(cos(PI * i / 180) * R + Left + R);
    y1 := Round(sin(PI * i / 180) * R + Top + R);
    x2 := Round(cos(PI * i / 180) * (R - (R / 100 * 7)) + Left + R);
    y2 := Round(sin(PI * i / 180) * (R - (R / 100 * 7)) + Top + R);
    bmpBuffer.Canvas.MoveTo(x1, y1);
    bmpBuffer.Canvas.LineTo(x2, y2);
    i := i + 30;
  end;
end;

procedure RotateBitmap180(ABitmap: TBitmap);
var
  bmpBuff: TBitmap;
  txfTag: tagXFORM;
  sngSin, sngCos: Single;
begin
  sngSin := Sin(DegToRad(180));
  sngCos := Cos(DegToRad(180));

  txfTag.eM11 :=  sngCos;
  txfTag.eM12 :=  sngSin;
  txfTag.eM21 := -sngSin;
  txfTag.eM22 :=  sngCos;
  txfTag.eDx  := (ABitmap.Width - ABitmap.Width * sngCos + ABitmap.Height * sngSin) / 2;
  txfTag.eDy  := (ABitmap.Height - ABitmap.Width * sngSin - ABitmap.Height * sngCos) / 2;

  bmpBuff := TBitmap.Create();
  try
    bmpBuff.Width  := Round(ABitmap.Width * Abs(sngCos) + ABitmap.Height * Abs(sngSin));
    bmpBuff.Height := Round(ABitmap.Width * Abs(sngSin) + ABitmap.Height * Abs(sngCos));

    SetGraphicsMode(bmpBuff.Canvas.Handle, GM_ADVANCED);
    SetWorldTransform(bmpBuff.Canvas.Handle, txfTag);
    BitBlt(bmpBuff.Canvas.Handle, 0, 0, ABitmap.Width, ABitmap.Height, ABitmap.Canvas.Handle, 0, 0, SRCCOPY);

    ABitmap.Assign(bmpBuff);
  finally
    bmpBuff.Free();
  end;
end;

procedure TfrmAnalogClock.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  RotateBitmap180(bmpBuffer); // Obrót birmapy o 180*
end;

procedure TfrmAnalogClock.Timer1Timer(Sender: TObject);
var
  Options: TClockOptions; // Deklarujemy zmienną z opcjami zegara

begin
  bmpBuffer := TBitmap.Create();
  bmpBuffer.PixelFormat := pf24bit;
  bmpBuffer.SetSize(250, 250);

  Options.HourWidth := 2;
  Options.HourColor := clBlack;
  Options.HourStyle := psSolid;
  Options.SecondWidth := 1;
  Options.SecondColor := clRed;
  Options.SecondStyle := psSolid;
  Options.MinuteWidth := 2;
  Options.MinuteColor := clBlack;
  Options.MinuteStyle := psSolid;
  DrawClockShield(bmpBuffer, 100, 20, 20, 4, clBlack);
  // Rysujemy tarczę zegarową na płótnie bitmapy formy kolorem czarnym i grubości 4 pikseli. Promień 100 pikseli, pozycja x i y 20 pikseli.

  DrawAnalogClock(Time, bmpBuffer, 100, 20, 20, Options);
  // Rysujemy zegar z aktualnym czasem na płótnie bitmapy. Promień 100 pikseli, pozycja x i y 20 pikseli.

  Caption := FormatDateTime('h:nn:ss', Time); // Wyświetlenie czasu w tytule formy

  frmAnalogClock.Canvas.Draw(0, 0, bmpBuffer);

  bmpBuffer.Free; // Czyścimy canvas formy
end;

end.

Wrzuciłem w zdarzenie OnMouseDown formy, aby bitmapa się obracała. Natomiast tak się nie dzieje, pewnie dlatego, że zegar jest cały czas malowany na nowo w Timerze.
Wcześniej pisałeś, aby wydzielić kod rysowania wskazówek do osobnej procedury, właśnie procedura

DrawAnalogClock

, odpowiada za rysowanie wskazówek zegara.

0

Część elementów musisz zadeklarować osobno i zapewnić do nich dostęp z poziomu metod klasy okna, zamiast przekazywać je w parametrach tych metod; To tyczy się przede wszytkim obiektu bitmapy, a także zmiennej z aktualnym czasem; Poza tym, Twój kod timera w kółko tworzy i zwalnia obiekt pomocniczej bitmapy - po co? Natomiast Twoje kliknięcie nie ma prawa działać, albowiem obiekt bmpBuffer istnieje w pamięci tylko w czasie wykonywania zdarzenia OnTimer obiektu timera (czyli raz na sekundę, przez kilka milisekund);

Wcześniej pisałeś, aby wydzielić kod rysowania wskazówek do osobnej procedury, właśnie procedura DrawAnalogClock odpowiada za rysowanie wskazówek zegara.

Tak, metoda DrawAnalogClock odpowiada za malowanie wskazówek; Jednak napisałem, abyś tą metodę podzielił na mniejsze fragmenty, wydzielając osobną metodę umożliwiającą namalowanie jednej wskazówki; Taką metodę należałoby wywołać trzy razy - po razie dla każdej wskazówki;

Kilka rzeczy, o których warto wspomnieć:

  • TClockOptions zawiera dziewięć pól, po trzy dla każdej wskazówki; Jednak te pola nie są pogrupowane; Sugeruję utworzyć sobie typ rekordu TClockHandOptions i w nim zawrzeć pola Width, Color oraz Length; To pierwsze to gruboś wskazówki, drugie to kolor, a trzecie to jej długość; Styl malowania nie jest potrzebny, bo wszystkie wskazówki to i tak linie ciągłe; Następnie poprawić typ TClockOptions i zadeklarować w nim trzy pola: HandHours, HandMinutes i HandSeconds o typie TClockHandOptions; Dzięki temu będziesz miał ustawienia pogrupowane, ale nie tylko;
  • wydziel kod malujący wskazówkę do osobnej metody; Niech pobiera w parametrach kąt wskazówki (w dowolnej mierze) oraz opcje wskazówki wcześniej zadeklarowanego typu TClockHandOptions; Dzięki temu taką metodę będziesz mógł wywołać trzy razy - po razie dla każdej wskazówki;
  • podczas malowania wskazówki korzystaj z zapamiętanego punktu środka tarczy, kąt wskazówki pobierz z parametru, natomiast grubość, kolor i długość wskazówki weź z drugiego parametru typu TClockHandOptions;
  • zapamiętaj środek zegara w dodakowej zmiennej (najlepiej w dodakowym polu klasy okna); Dzięki temu nie będziesz musiał w kółko obliczać środka tarczy przy malowaniu wskazówek;
  • pomocniczą bitmapę do malowania zegara trzymaj w polu klasy okna; Utwórz ją w konstruktorze formularza i zwolnij w destruktorze; Podczas malowania grafiki zegara najpierw ją czyść (FillRect kolorem białym), następnie namaluj markery godzin, na końcu trzy wskazówki;
  • zapamiętuj aktualny czas w dodatkowej zmiennej, zamiast wywoływać funkcję Time kilka razy; Dzięki temu czas wystarczy porbrać raz (w timerze) i wpisać go do tego pola, a podczas wykonywania metod malujących, do tego pola będzie dostęp, więc nie trzeba będzie przekazywać czasu w parametrach;
  • wydziel obliczanie kąta dla danej wskazówki do osobnej funkcji; Tą funkcję użyj do podania argumentu w metodzie DrawClockHand, malującej pojedynczą wskazówkę;
    To w sumie tyle na początek - w razie czego pytaj.

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