Dobra Panowie mam jeszcze jeden mały problem, z którym może mi coś podpowiecie bo nie do końca kumam z czego wynika problem (tzn. domyślam się, że chodzi o współrzędne punktów dla krzywej beziera) […]
Coś ta krzywa jest mało Bezierowa — wygląda bardziej jak PolyLine
niż PolyBezier
… A pisałem, że Pie
jest lepszy. :D
Utrudniasz sobie trochę robotę. Owszem, można obramowanie całości namalować za pomocą krzywej i dwóch prostych, ale to kupka zbędnych obliczeń, no i tło trzeba wypełnić później metodą FloodFill
. A tak to wystarczy ustawić kolory ramki oraz tła i wywołać jedną metodę Pie
, która od razu stworzy główny kształt wycinka.
Jak widać część punktów pokrywa się z osią ale niektóre nie.
Nie mam pojęcia dlaczego tak się dzieje, ale problemem nie jest długość promieni, a kształt krzywej — ta nie układa się w okrąg, miejscami jest spłaszczona. Dlatego na Twoim miejscu olałbym te krzywe i skorzystał z Pie
, bo jest znacznie prostszy w użyciu i faktycznie tworzy wycinek koła, bez żadnych spłaszczeń i innych mankamentów.
No dobra, teraz rozwiązanie — w załączniku masz źródła oraz skompilowanego exeka do zabawy. Apka pisana w Lazarusie, bo Delphi nie używam i nie mam go zainstalowanego, ale kod możesz przenieść do Delphi bez żadnych zmian.
Jak sugerowałem, najłatwiejszym sposobem na namalowanie wycinka koła jest użycie Pie
, z odpowiednio obliczonymi kątami. Można to zrobić na dwa sposoby — albo w pętli wołać Pie
i oprócz głównego kształtu, namalować od razu jego pierścienie, albo metodą Pie
namalować główny kształt, a metodami Arc
domalować pierścienie. Pierwsza opcja łatwiejsza, ale powoduje, że linie graniczne kształtu miejscami będą pogrubione. Druga pozwala uniknąć tego defektu, ale wymusza napisanie większej ilości kodu.
Na potrzeby testu, skorzystałem z tej pierwszej, czyli kształt i pierścienie renderowane są za pomocą Pie
w pętli:
procedure TMainForm.DrawRadarRings();
var
PieIndex, PiesCount, PieRadius: Integer;
PieStep: Single;
begin
PiesCount := FRadarRangeDistance div 100;
PieStep := FRadarRadius / PiesCount;
for PieIndex := PiesCount downto 1 do
begin
PieRadius := Round(PieIndex * PieStep);
MainForm.Canvas.Pie(
FRadarSpot.X - PieRadius,
FRadarSpot.Y - PieRadius,
FRadarSpot.X + PieRadius,
FRadarSpot.Y + PieRadius,
FRadarSpot.X + Round(PieRadius * Cos(DegToRad(90 - FRadarRangeAngle))),
FRadarSpot.Y - Round(PieRadius * Sin(DegToRad(90 - FRadarRangeAngle))),
FRadarSpot.X + Round(PieRadius * Cos(DegToRad(90 + FRadarRangeAngle))),
FRadarSpot.Y - Round(PieRadius * Sin(DegToRad(90 + FRadarRangeAngle)))
);
end;
end;
Pole FRadarRangeAngle
zawiera kąt będący wielokrotnością 10
, z zakresu od 10
(minimalny wycinek koła) do 180
(pełne koło). Użycie kąta 90
pozwala ustawić kąt 0° na północ, a nie jak domyślnie na wschód. Wycinki renderowane są od największego do najmniejszego, tyle razy ile radar ma mieć pierścieni dystansowych. Maksymalny dystans trzymany jest w polu FRadarRangeDistance
, zakres od 100
do 3000
(w mojej testowej aplikacji, można ustawić dowolny niezerowy).
Tak więc najpierw oblicza się liczbę pierścieni, dzieląc dystans przez 100
. Następnie oblicza się grubość pierścienia, dzieląc promień radaru ze zmiennej FRadarRadius
przez liczbę pierścieni. Wynik trzymany jest w postaci zmiennoprzecinkowej, aby uniknąć błędu zaokrąglenia. Następnie w pętli od największego do najmniejszego pierścienia, najpierw oblicza się promień wycinka mnożąc indeks pierścienia przez jego grubość (całość zaokrągla się na koniec), a następnie renderuje wycinek o zadanych kolorach tła i obramowania. Mniejsze wycinki przykrywają większe, co daje docelowo cały wycinek oraz linie pierścieni.
Druga część to renderowanie linii sekcji według kątów:
procedure TMainForm.DrawRadarSections();
var
LineIndex, LinesCount: Integer;
begin
LinesCount := (FRadarRangeAngle div 10) * 2 - 1;
for LineIndex := 1 to LinesCount do
MainForm.Canvas.Line(
FRadarSpot.X,
FRadarSpot.Y,
Round(FRadarSpot.X + Cos(DegToRad(270 + FRadarRangeAngle - LineIndex * 10)) * FRadarRadius),
Round(FRadarSpot.Y + Sin(DegToRad(270 + FRadarRangeAngle - LineIndex * 10)) * FRadarRadius)
);
end;
Najpierw oblicza się liczbę linii do namalowania. Zakres kątowy dzieli się przez 10
, uzyskując liczbę wycinków, mnoży się ją przez 2
aby dostać liczbę pól po obu stronach kąta 0° i odejmuje 1
, aby nie renderować linii na brzegu głównego wycinka (coby uniknąć sztucznego pogrubienia). To by miało znaczenie przy używaniu Arc
, ale że używam Pie
, to nie ma to znaczenia. Jednak jeśli wymieni się Pie
na Arc
, to powyższy kod się dostosuje.
No i tyle — w pętli renderuje się linie w kierunku odwrotnym do wskazówek zegara, zaczynając od kąta o 10° mniejszego niż kąt brzegowy.
Aplikację testową przygotowałem w taki sposób, że renderowanie radaru jest sparametryzowane — w dwóch kontrolkach edycyjnych ustala się zakres kątowy (rozmiar wycinka) oraz zakres dystansowy (liczba pierścieni), dzięki czemu można sprawdzić jak wygląda wykres dla różnych danych wejściowych.
Co prawda parametryzacja jest uboga, bo zakres kątowy jest wielokrotnością 10
, a dystansowy 100
, ale na potrzeby testu wystarczy. W razie czego łatwo ten kod przystosować do dowolnych zakresów kątowych i dystansowych, zamieniając w kodzie niektóre literały na wartości ustalane dynamicznie.
Nie wykryłem żadnych problemów z obliczaniem i renderowaniem promieni — te nie są ani za krótkie, ani za długie. Tak więc spokojnie możesz z tego kodu skorzystać i przystosować go do własnego projektu. Ale i tak sugeruję wymienić pętle z Pie
na jedno wywołanie Pie
i pętlę Arc
— wykluczy się sztuczne pogrubienie promieni brzegowych.
No, to 200zł się należy. :P