Autor Beitrag
IhopeonlyReader
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Mo 28.01.13 20:55 
Guten Tag,
Ziel: Prozedure erstellen, die:
mit folgenden Parametern: Kreismittelpunkt (M), Radius (r), Canvas (BC)
folgendes tut:
- auf ein jetziges Canvas (z.B. Bitmap.Canvas woebei auf der bitmap ein sich bewegendes bild ist) den Umriss eines Kreises zeichnet...
- schneller als 5ms ist (bei einer Berechnung von einem radius bis zu 300 (pixeln))

Bis jetzt ist es meistens am 2ten gescheitert....

Folgendes habe ich ausprobiert:
Zuerst: normaler Kreis mit "durchsichtiger"/"weißer" farbe gefüllt und farbigen rand...
Problem: bei weißer mitte: bitmap im kreis überzeichnet bei durchsictiger mitte: mitübergebene canvas fehlte ebenfalls in dem kreis.. in dem kreis war mein desktophintergrund zu sehen

Danach: (siehe de.wikipedia.org/wik...oordinatengleichung)
2 Forschleifen,wobei die eine von m.x-r bis m.x+r und die andere (in der erstn) von m.y-r bis m.y+r geht... danach der entsprechende die strecke (Sr) von punkt (a|b) (a = durchlaufsvariable aus der ersten for-schleife (x-koordinate) (b= durchlaufsvariable aus der zweiten for-schleife (y-koordinate) zum mittelpunkt mit dem satz des pytagoras berechnet wird und wenn Sr = r dann wird der pixel an dem punkt (a|b) gezeichnet. .. (also viereck pixel für pixel durchgehen und berechnen ob der pixel die entfernung des pixels zum mittelpunkt = der radius ist)
Problem: dauert zu lang... klappt aber einwandfrei

Danach: (siehe de.wikipedia.org/wik...3.A4ndiger_Oktanten)
Berechnung nach dem Ansatz nach der Methode von Horn.. (8 punkte berechnen (obem mitte, unten mitte, mitte rechts, links mitte und entsprechend wenn man r in 2 gleichlange seiten unterteilt (r^2 =sx^2+sy^2))
Problem: Es sind nur 8 Punkte , also ein 8 Eck... bei größeren radien (ab 100) sehr ungenau/unübersichtlich (starke abweichungen) wenn man geraden zwischen den punkten zieht

ERgänzung:
danach habe ich die Methode von Horn weiter verfolgt..
Horn hat nun diese 8 Punkte rotieren lassen (siehe "Kreisvariante des Algorithmus" (im link)) mit einer while schleife....
Problem: ich habe das Beispiel nicht ganz verstanden und damit falsch ungesetzt...


Letzter Versuch:
Punktberechnung und mit sinus den punkt, wie an einer schnur, um den mittelpunkt kreisen lassen...
Teilproblem: Mal hats mit dem 5 ms geklappt mal nicht... Sinus und Cosinus sind in Delphi sehr langsam in der Berechnung...

Könnt ihr mir bei der Methode von Horn oder bei meinem letzten Ansatz helfen?

Freue mich auf Ideenreiche antworten


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Mo 28.01.2013 um 22:41

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mo 28.01.13 21:19 
Hallo,
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
- schneller als 5ms ist (bei einer Berechnung von einem radius bis zu 300 (pixeln))

Ich verstehe nicht richtig, was Du eigentlich meinst.
Bei einem Test habe ich 2000(!) Kreise mit Füllen in 200 ms auf den Bildschirm gezeichnet und dabei waren noch 6000 Random-Rufe inklusive. Mit durchsichtigem Brush waren es 80 ms.
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
bei durchsictiger mitte: mitübergebene canvas fehlte ebenfalls in dem kreis.. in dem kreis war mein desktophintergrund zu sehen

Und das verstehe ich gar nicht. Ein Kreis mit brush.style:=bsclear zeichnet den Rand, wie Du es ja wünschst.
Vielleicht zeigst Du einmal etwas Quelltext. Denn ich vermute, dass Problem liegt nicht beim Kreis.

Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
IhopeonlyReader Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Mo 28.01.13 21:37 
normale kreise ja :D wie gesagt hierbei lag das problem nicht bei der geschwindigkeit... sondern bei dem überzeichnen der mitte
5ms bei r=300 ... logisch was damit gemeint ist oder? es soll ein kreisumriss gezeichnet werden, bei dem r bis auf 300 hochgeht.. die zeichenzeit darf 5 ms nicht überschreiten..

durchsichtige mitte = transparent color (form durchsichtbarkeit) -> durchsichtige form


brush.style := bsclear
DAS ist das was ich suchte :).. ich hatte die brush.color auf die transparent color der form gesetzt (Form1.TransparentColor := True;
Form1.TransparentColorValue := clyellow; und dann brush.color := clyellow... wobei ich natürlich eine ungebräuchlichere farbe als gelb verwendet hatte...)

da ich brush.style := bsclear nicht kannte versuchte ich mir mit all den anderen formen und berechnungen zu helfen um den umriss herauszufinden

(auch wenn ich sagen muss meine methode mit den 2 for-schleifen liefert ein "runderen" kreis^^ (raidus = 250 pixel)

aber DANKE

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Di 29.01.13 19:15 
Hallo,

man kann Kreise auch anders zeichnen. Ich hatte mal Überlegungen, bei denen es geblieben ist, einen Stiftplotter zu bauen und da kann man Bresenham für Kreise nicht so wirklich anwenden.
Also habe ich eine gewisse Abweichung zugelassen und dann den Linienzug bestimmt, den man über Additionstheoreme von sin und cos sehr schnell berechnen kann.Es war mal für TurboPascal und ist hier auf eine Form mit einem Button umgesetzt.Natürlich ist das nicht so schnell, wie die Grafikarte via GDI+ oder DirectX das könnte, aber auch nicht extrem langsam. 8 Sekunden für 10000.
KreisLinienzug

ausblenden volle Höhe Delphi-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:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}
type
        tKreisDat = record
                      rcosPhi,
                      rsinPhi,

                      dCosPhi,
                      dSinPhi :  double;
                      r,
                      x_m,
                      y_m,
                      next_x,
                      next_y,
                      countdown : word;

                      fertig : boolean;
                    end;


function KreisInitLinie(x,y,r:word;
                        phi_start,phi_end:double;// Winkel in Grad
                        delta:double=0.25): tKreisDat;
{Startwerte fuer einen bestimmten Kreis berechnen}
{der eine maximale Abweichung von delta Punkten der Linie von der Kreisform hat}
{ergibt nur den ersten Punkt}
const
  DegToRad = pi/180;
  VollKreis = 360.0;//°
var
  Phi,
  dPhi,
  DeltaPhi : double;
begin
result.r := r;
with result do
  begin
  x_m := x;
  y_m := y;
  countDown := 0;
  {Nicht mehr, als einen Vollkreis zeichnen}
  deltaPhi := phi_end-Phi_start;
  IF ABS(deltaPhi) > VollKreis then
    IF deltaPhi > 0 then
      deltaPhi := Vollkreis
    else
      deltaPhi := -VollKreis;

  deltaPhi := deltaPhi*DegToRad;
  Phi := phi_start*DegToRad;

  {Ab hier in rad }
  if r> delta then
    begin
    {Bestimmung des halben Winkels, bei dem die Abweichung kleiner delta bleibt}
    rcosPhi := (r-delta)/(r+delta);
    rsinPhi := sqrt(1-sqr(rcosPhi));
    {Schrittwinkel ist dann doppelt so grosz}
    dSinPhi := 2*rcosPhi*rsinPhi;
    dCosPhi := sqr(rcosPhi)-sqr(rsinPhi);
    IF dCosPhi > 0 then
      dPhi := ArcTan(dSinPhi/dCosPhi)
    else
      dPhi := pi/4;
    {Ganzzahliges Vielfaches vom Differenzwinkel}

    //countdown muss positiv bleiben
    countDown := round(ABS(deltaPhi)/dPhi+0.5);
    dPhi := deltaPhi/countdown;
    end
  else
    dphi   := 2*pi;

  {Zu diesem Delta-Winkel sin und cos bestimmen}
  dSinPhi := sin(dPhi);
  dCosPhi := cos(dPhi);

  {Startwerte}
  {Radius direkt reinmultipliziert}
  rCosPhi := r*cos(phi);
  rSinPhi := r*sin(phi);

  next_x := x_m+round(rcosPhi);
  next_y := y_m-round(rsinPhi);
  {Bei Radius = 0 gibt es nichts zu zeichnen }
  fertig := r= 0;
  end;
end;

procedure nextPunkt(var KreisDat:TKreisDat);
var
  tmpCos : double;
begin
with kreisdat do
  begin
  if fertig then
    exit;
  tmpCos := rcosPhi;
  rcosPhi:= rcosPhi*dcosPhi-rsinPhi*dsinPhi;
  rsinPhi:= rsinPhi*dcosPhi+tmpCos*dsinPhi;
  {Neuer Endpunkt}
  next_x := x_m+round(rcosPhi);
  next_y := y_m-round(rsinPhi);
  {Wie weit bisher gezeichnet wurde}
  dec(countdown);
  fertig := (countdown = 0);
  end;
end;

Procedure KreisLinienweise(var Kreis:TKreisDat;
                           cv : TCanvas;
                           Kcolor: tColor);
{$DEFINE LinienAbschnittesichtbar}
begin
cv.Pen.Color:= Kcolor;
with Kreis do
  begin
  cv.Moveto(Next_x,Next_y);
  repeat
    NextPunkt(kreis);
    cv.Lineto(Next_x,Next_y);
{$IFDEF LinienAbschnittesichtbar}
    Kcolor := Not KColor;
    cv.Pen.Color:= Kcolor;
{$ENDIF}
  until fertig;
  end;
end;

{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
const
  von = 0;
  bis = 255-von;
var
  Kreis : TKreisDat;
  xm,ym,
  i,j,
  delta,r : integer;
begin
  j := von;
  delta := 1;

  xm := Form1.ClientWidth DIV 2;
  ym := Form1.ClientHeight DIV 2;

  r := xm;
  IF xm > ym then
    r := ym;

  For i := 1 to 10000 do
    begin
    Button1.Caption := INtToSTr(i);
    Kreis := KreisInitLinie(xm,ym,r-round(0.5*r*random),0.0,72360.0);
    KreisLinienweise(Kreis,Form1.Canvas,j*(256*256+256+1));
    //auf und abschwellende Grautoene
    inc(j,delta);
    IF (j <= von) or (j >= bis) then
      delta := -delta;
    end;

end;
end.



Eine Skizze wäre wohl angebracht ;-)
EDIT:
Hier sieht man, dass der Kosinus des halben Winkels einer Linie mit der maximalen Abweichung delta entsprechend so berechnet wird:
cos(dPhi/2) = (r-delta)/(r+delta)

WinkelbestimmungKreis
Einloggen, um Attachments anzusehen!
Kostas
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Do 07.02.13 17:02 
Hallo,

nur als Ergänzung gedacht...
Wenn es um das Zeichnen geht, ähnlich einem CAD-System, gibt es die ImageEN Komponenten. Damit kannst du Verktoren zeichnen und hast ein Panel welches Zoomfähig ist.

Gruß Kostas