Rysowanie obszarów przy pomocy wzoru - zmiana na rysowanie granic tych obszarów

0

Witam serdecznie, na wstępie zaznaczę, że kod nie jest mojego autorstwa, jednakże zostałem poproszony o przerobienie go przez samego autora. Nie jestem tak zaawansowany w programowaniu w delphi jak ten człowiek, jednakże posiadam jakieś umiejętności a on nie ma czasu i poprosił mnie, dlatego nie chce udostępniać za dużo kodu jeśli nie jest to konieczne ;) Praca nie ma charakteru komercyjnego.

Mam napisaną taką pętlę (a raczej dwie pętle):

for  i:=-300 to 300 do
begin
     for k:=-200 to 200 do
     begin

Przed nią jest kilka różnych deklaracji zmiennych, kilka warunków itp., nie są one raczej istotne.

 
dr2:=abs(0.0175*((1/sin(2*(arctan(sqrt(((p2-ra2)*(p2-rb2))/(p2*(p2-d2)))))))*(sqrt(sqr((ra2)*(da1))+sqr((rb2)*(db1))))));

W zależności od pewnych wartości wyliczonych z w/w wzoru rysuje mi się obszar o danym kolorze, ograniczony tymi wartościami, są to kształty różnego rodzaju, w zależności od kilku innych warunków. Przykładowym kształtem jest np. lekko zdeformowana (spłaszczona) "8".

Do rysowania wykorzystuje metodę pixels:

       if (dr2>20) and  (dr2<50) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clpurple;

Gdzie poczx i poczy to po prostu początek canvasu wygenerowanego przy wywoływaniu rysowania tych obszarów. Działanie jest dosyć trywialne, metoda ta rysuje piksel o danym kolorze w konkretnym miejscu. Moje pytanie brzmi, czy jest możliwość aby nie rysowało tych pikseli wszędzie a jedynie w miejscu gdzie jest granica tych obszarów.

Oto przykład co chce uzyskać (po prawej) oraz jak jest (po lewej):

ex.PNG

Z góry dziękuję za wszelkie wskazówki, pomoc itp.
Pozdrawiam

2

Zależy od wzoru...
Ogólnie, to zamiast czegoś takiego:

for x := 0 to 100 do
 for y := 0 to 100 do
  if (policz_costam(x, y)) then
   narysuj_piksel(x, y);

Powinieneś mieć postać:

for i := costam to costam_2 do
 narysuj_piksel(policz_x(i), policz_y(i));

Lecz jak powiedziałem: zależy od wzoru :P

2
Canvas.Brush.Style := bsClear;

Edit:
Źle pisze. jeśli rysujesz po pixelach to musisz zmienić wzór jak @Patryk27 mówi

0

Nie no, tak to raczej nie zadziała ;)

To by miało jak najbardziej sens jakbym miał prostą funkcję typu y=ax+b i zamiast rysować ją powiedzmy w całym jej przedziale to narysowałbym od -5 do 5, taki wycinek prostej ;)

Dziękuję za zainteresowanie - czekam na dalsze propozycje :)

2

Zamiast rysować piksele zrób sobie tablicę punktów, do której będziesz wpisywał kolejne punkty obliczone ze wzoru; potem wybierz punkty brzegowe i jedynie te narysuj. Lecz może być kiepsko z wydajnością.

0

może zamiast warunku if (dr2>20) and (dr2<50) then spróbuj if (dr2==20) or (dr2==50) then

Jeśli dr2 jest liczbą rzeczywistą to zaokrąglij ją/utnij część dziesiętną przed warunkiem.

0

Wtedy rysuje mi praktycznie cały canvas, także to nie to ;)
Tablicami średni pomysł, bo wydajnościowo pixels już jest słabe a tutaj musiałbym jeszcze zrobić kilka tablic albo jakąś wielowymiarową.

0

pokaż całą drabinkę if'ów z obraz.Canvas.pixels[poczx+i,poczy-k]:=clpurple; tutaj ograniczane są pola od do jaki kolor ma być malowany i to jest klucz, zaokrąglij dr2 do liczby całkowitej

0
vBB napisał(a):

Tablicami średni pomysł, bo wydajnościowo pixels już jest słabe a tutaj musiałbym jeszcze zrobić kilka tablic albo jakąś wielowymiarową.

Pixels to nie jest tablica, a property, które jest zadeklarowane następująco:

property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;

GetPixel/SetPixel są powolne, ponieważ występują w nim odwołania do API Windowsa:

function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
  RequiredState([csHandleValid]);
  GetPixel := Windows.GetPixel(FHandle, X, Y);
end;

procedure TCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
  Changing;
  RequiredState([csHandleValid, csPenValid]);
  Windows.SetPixel(FHandle, X, Y, ColorToRGB(Value));
  Changed;
end;
0

Tutaj cały kod pętli:

for  i:=-300 to 300 do
begin
     for k:=-200 to 200 do
     begin

           ra2:=sqrt(sqr(i-xa1)+sqr(k-ya1));
           rb2:=sqrt(sqr(i-xb1)+sqr(k-yb1));
           d2:=sqrt(sqr(xb1-xa1)+sqr(yb1-ya1));
           p2:=(ra2+rb2+d2)/2;


            if p2=d2 then
              begin
                   obraz.canvas.pen.mode:=pmcopy;
                   obraz.canvas.pen.color:=clnone;

                   obraz.canvas.rectangle(poczx+i,poczy-k,poczx+i+50,poczy-k-50);
                   end;

           if p2<>d2 then
              begin
                   obraz.canvas.brush.style:=bssolid;
				   
                        if ((((p2-ra2)*(p2-rb2))/(p2*(p2-d2))))<=0 then
                        obraz.Canvas.pixels[poczx+i,poczy-k]:=clsilver;

                        if (((p2-ra2)*(p2-rb2))/(p2*(p2-d2)))>0 then
                        begin
                        
						if (2*(arctan(sqrt(((p2-ra2)*(p2-rb2))/(p2*(p2-d2))))))=0 then
                        begin

                        obraz.Canvas.pixels[poczx+i,poczy-k]:=clred;
                        end;
						
                        if (2*(arctan(sqrt(((p2-ra2)*(p2-rb2))/(p2*(p2-d2))))))<>0 then
                        begin

              // dr2:=abs(0.0175*((1/sin(2*(arctan(sqrt(((p2-ra2)*(p2-rb2))/(p2*(p2-d2)))))))*(sqrt(sqr((ra2)*(da1))+sqr((rb2)*(db1))))));
               dr2:=abs(0.0175*((1/sin(2*(arctan(sqrt(((p2-ra2)*(p2-rb2))/(p2*(p2-d2)))))))*(sqrt(sqr((ra2)*(da1))+sqr((rb2)*(db1))))));

                  if (db1+da1>=2)  then
                  begin
                 if (dr2>20) and  (dr2<50) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clpurple;
                 if (dr2>10) and (dr2<20) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clolive;
                   if (dr2>5) and (dr2<10) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clnavy;
                   if (dr2<5) and (dr2>1) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=clblue;
                   if (dr2<1) and (dr2>0.5) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=claqua;
                   if (dr2<0.5) and (dr2>0.1) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clgreen;
                   if (dr2<0.1) and (dr2>0.01) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=cllime;
                     if (dr2<0.01) then
                    obraz.canvas.pixels[poczx+i,poczy-k]:=clgray;
                     end;
                     if (db1+da1<2)  then
           
                if (dr2>5) and (dr2<10) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clnavy;
                   if (dr2<5) and (dr2>1) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=clblue;
                   if (dr2<1) and (dr2>0.5) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=claqua;
                   if (dr2<0.5) and (dr2>0.1) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clgreen;
                   if (dr2<0.1) and (dr2>0.01) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=cllime;
                     if (dr2<0.01) then
                    obraz.canvas.pixels[poczx+i,poczy-k]:=clgray;
                   if dr2=dr1 then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clsilver;

                  end;
               end;
          end;

     end;
end;
 

Przed pętlą jest zadeklarowanych kilka wzorów, matematyka sama, stąd te warunki.

Patryk27 napisał(a):

Pixels to nie jest tablica, a property, które jest zadeklarowane następująco:

Wiem, przecież nie napisałem, że to tablica, a jedynie, że pixels jest powolne ;) A tablica sprawiłaby, że jeszcze program zwolni.

EDIT: Trochę zmyliłem na początku, bo poczx i poczy to nie współrzędne początku canvasu tylko współrzędne początku rysowania tych kolorowych obszarów, są one zależne od tego gdzie ustawi je użytkownik.

0

No niestety moim sposobem nie dostaniesz konturów tylko co najwyżej miejsca brzegowe, czyli kontur będzie w jednym miejscu grubszy w innym węższy. Pozostaje chyba wykorzystać jakiś algorytm wypełniania kolorem.
Chyba, że... wykorzystując fakt, że piksele rysowane są od lewej do prawej

  1. zapamiętasz sobie kolor rysowania 1 piksela
  2. kolejny piksel przed narysowaniem sprawdzi czy kolor jest inny od zapamiętanego w kroku 1 jeśli tak rysuje go i zmienia "zapamiętany" kolor na nowy, jeśli kolory są identyczne to rysuje biały lub nie rysuje nic.
1

myśle że gdyby w tych ifach umiejetnie pozmieniać <, >, na = to uzyskał być pożądany efekt.

1

Wiem, przecież nie napisałem, że to tablica, a jedynie, że pixels jest powolne A tablica sprawiłaby, że jeszcze program zwolni.

Właśnie, że nie. Zastosowanie tablicy pozwoli rysować przy pomocy scanline, co znacznie przyśpieszy działanie.
A co do rysowania tylko brzegu, to na szybko przychodzi mi na myśl edycja zaznaczenia w photoshopie o nazwie: contract selection. Zaznaczony obszar kurczy się do wnętrza x pikseli i można go usunąć. Coś takiego bym szukał w google, bo rozumiem modyfikacja wzoru odpada.

1

zrób tablice i w niej to narysuj i potem SetDIBits, podepniesz ją pod Twoj obrazek.

0
szopenfx napisał(a):

No niestety moim sposobem nie dostaniesz konturów tylko co najwyżej miejsca brzegowe, czyli kontur będzie w jednym miejscu grubszy w innym węższy. Pozostaje chyba wykorzystać jakiś algorytm wypełniania kolorem.

Tę metodę znam, nie działa w każdej sytuacji z racji różnego położenia dwóch punktów startowych poczx i poczy.

szopenfx napisał(a):

Chyba, że... wykorzystując fakt, że piksele rysowane są od lewej do prawej

  1. zapamiętasz sobie kolor rysowania 1 piksela
  2. kolejny piksel przed narysowaniem sprawdzi czy kolor jest inny od zapamiętanego w kroku 1 jeśli tak rysuje go i zmienia "zapamiętany" kolor na nowy, jeśli kolory są identyczne to rysuje biały lub nie rysuje nic.

J/w, tj. czy nie będzie problemu, że punkty początkowe są konfigurowane tak, że mogą być w różnych pozycjach? Plansza ma 400x600 pikseli przy czym położenie tych punktów początkowych może być dowolne.

m_Lesiu napisał(a):

myśle że gdyby w tych ifach umiejetnie pozmieniać <, >, na = to uzyskał być pożądany efekt.

Próbowałem, było by ok jakby obiekty były zawsze w tym samym miejscu.

ergo napisał(a):

A co do rysowania tylko brzegu, to na szybko przychodzi mi na myśl edycja zaznaczenia w photoshopie o nazwie: contract selection. Zaznaczony obszar kurczy się do wnętrza x pikseli i można go usunąć. Coś takiego bym szukał w google, bo rozumiem modyfikacja wzoru odpada.

Ale czy w Delphi coś takiego w ogóle istnieje?

m_Lesiu napisał(a):

zrób tablice i w niej to narysuj i potem SetDIBits, podepniesz ją pod Twoj obrazek.

Mógłbyś coś więcej, dokładniej, o tym napisać?

1

Ustaliliśmy, że nie będziemy ruszać wzoru który wylicza nam które pixele mają być malowane i padła propozycja, żeby 'odfiltrować' niechciane pixele. Przechowujesz informacje o kolorach pixeli w Canvas.Pixels do których dostep troche trwa. Dlatego proponuje, żebyś stworzył tablice

type
  TRGB = record
    r,
    g,
    b : byte;
  end;
ArrPixels = array[ 0..399, 0..599 ] of TRGB;

wypełnij tą tablice tak jak wypełniasz teraz Canvas.Pixels. nastepnie odfiltrował niechciane kolory ( sposób @szopenfx wydaje sie byc prosty i skuteczny ) i majac już 'obrazek' w pamięci wstawić go jako bitmape. Funkcja SetDIBits pozwala wstawić tablicę
kolorów do bitmapy. ( The SetDIBits function sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB. )

0
m_Lesiu napisał(a):

nastepnie odfiltrował niechciane kolory ( sposób @szopenfx wydaje sie byc prosty i skuteczny )

No ale dla mnie niechcianym kolorem, np. będzie niebieski - tylko, że taką metodą usunąłbym wszystkie niebieskie pixele, włącznie z granicznymi które chce usunać. Czy jednak się mylę i @szopenfx ma racje z tym wyborem pixeli?

1

Jedziesz sobie wierszami po gotowym obrazku ktory przykładowo wyglada tak
0 - kolor biały, N - kolor niebieski

0000000000000000NNNNNNNNNNNNNNNN0000000000000000000

patrzysz ze na pozycji 12 masz kolor N, wiec od pozycji 13 zmieniasz N na 0,
jedziesz sobie 14 15 16 ... az tu NAGLE !!!!
na pozycji 28 masz znowu 0 wiec od pozycji 26 zaprzestajesz zmianiana kolorow
w efekcie powinnien Ci zostac pierwszy i ostatni napotkany niebieski pixel w kazdej linijce.

0000000000000000N00000000000000N0000000000000000000

uwaga dla pierwszej linijki:
algorytm powinien ruszyć dopiero w nastepnym wierszu po wykryciu koloru.

0

Aha, w ten sposób, no ale czy to zadziała w przypadku np. 8 kolorów? Tyle potrzebuję bo tyle używa teraz program.

1

no wtedy się troche skomplikuje. musiał byś dodać tablice dynamiczną. i po wykryciu każdego nowego koloru dodawać pole do tabelki i zamiętać dla kazdego koloru czy już był w tej linijce.

Edit;
Albo może być i statyczna skoro to tylko 8 kolorów;

0

Ok, tylko czy cały algorytm zadziała również w tym przypadku:

15fma7s.png

Zgodnie z tym co mówisz, powinno, aczkolwiek zanim zacznę pisać chce się dowiedzieć jak najwięcej. Trochę mi z tym zejdzie z racji niedużego doświadczenia, stąd moje obawy ;)

dodanie obrazu do treści posta - fp

2

Powinno zadziałać. Po prostu żadnych Continue w pętli nie używaj.

ale mysle, że
zmiana

 if (db1+da1>=2)  then
                  begin
                 if (dr2>20) and  (dr2<50) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clpurple;
                 if (dr2>10) and (dr2<20) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clolive;
                   if (dr2>5) and (dr2<10) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clnavy;
                   if (dr2<5) and (dr2>1) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=clblue;
                   if (dr2<1) and (dr2>0.5) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=claqua;
                   if (dr2<0.5) and (dr2>0.1) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clgreen;
                   if (dr2<0.1) and (dr2>0.01) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=cllime;
                     if (dr2<0.01) then
                    obraz.canvas.pixels[poczx+i,poczy-k]:=clgray;
                     end;
                     if (db1+da1<2)  then
 
                if (dr2>5) and (dr2<10) then
                     obraz.Canvas.pixels[poczx+i,poczy-k]:=clnavy;
                   if (dr2<5) and (dr2>1) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=clblue;
                   if (dr2<1) and (dr2>0.5) then
                     obraz.canvas.Pixels[poczx+i,poczy-k]:=claqua;
                   if (dr2<0.5) and (dr2>0.1) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clgreen;
                   if (dr2<0.1) and (dr2>0.01) then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=cllime;
                     if (dr2<0.01) then
                    obraz.canvas.pixels[poczx+i,poczy-k]:=clgray;
                   if dr2=dr1 then
                     obraz.canvas.pixels[poczx+i,poczy-k]:=clsilver;

wszystkich < i > na = tez by pomogła. i było by ładniej.
uzywając <, > rysujesz cały przedział od do, a przy = będzie tylko kontur.

2

To, co podałeś @TLesiu można nieco przyspieszyć; Jeśli już koniecznie mają pozostać takie drabinki, to do każdego if trzeba dodać else - inaczej to każdy warunek w drabince zostanie sprawdzony, nawet jeśli wcześniejszy został spełniony i obsłużony.

0
m_Lesiu napisał(a):

Powinno zadziałać. Po prostu żadnych Continue w pętli nie używaj.

ale mysle, że
zmiana
wszystkich < i > na = tez by pomogła. i było by ładniej.
uzywając <, > rysujesz cały przedział od do, a przy = będzie tylko kontur.

To było pierwsze o czym pomyślałem, niestety ta metoda się nie sprawdza, nie rysuje niczego, brak konturów i obszarów.

2

wszystkich < i > na = tez by pomogła. i było by ładniej.
uzywając <, > rysujesz cały przedział od do, a przy = będzie tylko kontur.

Liczb zmiennoprzecinkowych (czy raczej precyzując: liczb zmiennoprzecinkowych powstałych w wyniku jakichś operacji) nie powinno się porównywać ze sobą za pomocą operatora równości.
@vBB: spróbuj to porównać za pomocą CompareValue - ono troszczy się specjalnie o liczby zmiennoprzecinkowe.

0
Patryk27 napisał(a):

@vBB: spróbuj to porównać za pomocą CompareValue - ono troszczy się specjalnie o liczby zmiennoprzecinkowe.

Hm, w jaki sposób to zrobić? Musiałbym porównać wartość zmiennoprzecinkowego "dr2" do dwóch zwykłych integerów, np. 5 i 1 (if (dr<5) and (dr>1) then...).

1

W sensie - chcesz sprawdzić równość bądź czy ten float znajduje się w zbiorze 1..5?
Jeżeli sprawdzić równość, to coś w stylu: if (CompareValue(dr2, 1, Epsilon) = EqualsValue) Then (gdzie Epsilon to naprawdę mała liczba, np.0.00000001).
Jeżeli sprawdzić, czy znajduje się w zbiorze, to zwykłe: if ((dr2 >= 1) and (dr2 <= 5)) Then
Jeżeli sprawdzić, czy znajduje się poza zbiorem: if (dr2 < 1) or (dr2 > 5) Then

0

Zauważyłem taką rzecz, że dr2 nie osiąga nigdy wartości podanych w tych if'ach przeze mnie, tj. nigdy nie będzie miało równo 5 i równo 1 (jedynie wartości bardzo zbliżone, z wieloma miejscami po przecinku), stąd rysowanie konturów zmieniając warunki <,> na = raczej nie zadziała, chyba, że macie jakiś pomysł?

3

Zwiększ epsilon.

0

Zwiększyć czy dodać miejsc po przecinku (zmniejszyć)?

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