Entwickler-Ecke

Multimedia / Grafik - Einen Kreis pixelweise zeichnen -> Spline


F34r0fTh3D4rk - Sa 17.09.05 11:14
Titel: Einen Kreis pixelweise zeichnen -> Spline
Hallo, ich hatte vor einen Kreis, pixelweise zu zeichnen, also möglichst viele punkte zu berechnen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  r: integer;
begin
  r := 5;
  for i := 1 to 360 do
    image1.Canvas.pixels[round(cos(i)*r), round(sin(i)*r)]
end;

nur ist jetzt kein kreis zu sehen, ich habe ja auch nicht die kontrolle über die position des kreises, wie ist das richtig ?

wenns fertig ist, soll es ne art spline werden, an dem dann ein objekt langwandert, vielleicht das wollte ich dann für jede iteration berechnen, halt so wie im mini beispiel oben. da stellt sich mir auch noch die frage wie ich das dann mit der geschwindigkeit mache, hat da jemand ahnung von ?


Raphael O. - Sa 17.09.05 11:24


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  r: integer;
  x,y:integer;
begin
  x := 100;
  y := 100;
  r := 5;
  for i := 1 to 360 do
    image1.Canvas.pixels[x+round(cos(degtorad(i))*r), y+round(sin(degtorad(i))*r)]
end;


mit x = mittelpunkt in x-richtung und y = mittelpunkt in y-richtung

ungetestet, hoffe aber, dass es funktioniert..
wenn ich mich recht erinnere erwartet cos() und sin() gradangaben im bogenmaß, was durch degtorad() erreicht wird.


F34r0fTh3D4rk - Sa 17.09.05 11:41

ja so (oder so ähnlich) hab ich das auch schon getestet, geht leider net :(


Raphael O. - Sa 17.09.05 11:55


Delphi-Quelltext
1:
image1.canvas.pixels[x,y]:=clBlack;                    

weist du auch ne Farbe zu? ist oben in deinem Quelltext nicht der Fall


F34r0fTh3D4rk - Sa 17.09.05 12:10

LOL stimmt, wie dumm von mir :wink:

jetzt gehts auch:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.Button1Click(Sender: TObject);  
var
  i: integer;
  r: integer;
begin
  r := 15;
  for i := 1 to 360 do
    image1.Canvas.pixels[round(cos(i)*r) + r, round(sin(i)*r) + r] := clblack;
end;

ist das denn so auch korrekt mit den 360 ?

der kreis soll sich nämlich kreisförmig aufbauen, also der erste pixel oben bei 0° dann im uhrzeigersinn bis 360° Das tuts im moment net


Raphael O. - Sa 17.09.05 12:50

cos(0) = 1
sin(0) = 0
=> es fängt rechts an

wenns oben anfangen soll solltest du cos(i-90) und sin(i-90) nehmen
cos(-90)=0
sin(-90)=-1


F34r0fTh3D4rk - Sa 17.09.05 13:16

user profile iconRaphael O. hat folgendes geschrieben:
cos(0) = 1
sin(0) = 0
=> es fängt rechts an

wenns oben anfangen soll solltest du cos(i-90) und sin(i-90) nehmen
cos(-90)=0
sin(-90)=-1

macht er trotzdem nicht, das bild baut sich so zwischendrin stückweise auf, also mal hier n pixel, mal da, aber nicht linear.


Diabele$ - Sa 17.09.05 13:26

Das liegt daran, dass Delphi meint, dass die Winkelangaben im Bogenmaß stehen, und nicht im Gradmaß.


F34r0fTh3D4rk - Sa 17.09.05 13:54

hasse resch, so gehts:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  r: integer;
  x,y:integer;
begin
  x := 100;
  y := 100;
  r := 15;
  for i := 1 to 360 do
    begin
      image1.Canvas.pixels[x+round(cos(degtorad(i))*r), y+round(sin(degtorad(i))*r)] := clblack;
      application.processmessages;
      sleep(1);
    end;
end;


DANKE SCHÖN


alzaimar - So 18.09.05 08:40

Vielleicht sollte man sich mit Breshenham's Circle Algorithm beschäftigen. Der Zeichnet einen Kreis ohne floating point (sin, cos) arithmetik, sonder nur mit Hilfe von Integermathematik.


NCortex - So 18.09.05 15:23

schon, aber der baut den nicht kreisförmig auf...

sondern an 4 ecken.... (von dem kreis, is doof ich weiß)


alzaimar - Mo 19.09.05 16:43

Pruuuust. Zuerst dachte ich, du hättest'n Rad ab (4 Ecken bei einem KREIS???), aber dann habichs kapiert. Du willst den Kreis wirklich von 0-360° zeichnen. Die bisherigen Lösungen funktionieren nicht, wenn der Radius zu gross wird.

Ich würde folgendes machen:
Entweder gehst du zum 1.Punkt mit MoveTo und ab da zu den Folgepunkten mittels LineTo
Oder, Du baust Dir ein Array [0..460,0..1] Of Integer auf, packst da die Punkte rein und dann malst du es mit Canvas.Polygon oder so.
Oder, Du erstellst per Breshenham einen 1/8el Kreis als Punkteschar (1..n) und erstellst 8 Kopien davon, jeweils um 45° gedreht und abwechselnd gespiegelt, wenn Du verstehst, was ich meine.


uall@ogc - Mo 19.09.05 17:20


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
const r = 50;
      mx = 100;
      my = 100;
var x,y,y2: integer;
begin
  form1.Canvas.Pen.Color := clblack;
  for x := 0 to r-1 do
  begin
    y := round(sqrt(r*r-x*x));
    y2 := round(sqrt(r*r-(x+1)*(x+1)));
    form1.Canvas.MoveTo(x+mx, y+my);
    form1.Canvas.LineTo(x+1+mx, y2+my);
    form1.Canvas.MoveTo(-x+mx, y+my);
    form1.Canvas.LineTo(-x-1+mx, y2+my);
    form1.Canvas.MoveTo(x+mx, -y+my);
    form1.Canvas.LineTo(x+1+mx, -y2+my);
    form1.Canvas.MoveTo(-x+mx, -y+my);
    form1.Canvas.LineTo(-x-1+mx, -y2+my);
  end;
end;


mit hilfe des satzes des pythagoras


F34r0fTh3D4rk - Do 22.09.05 17:41

ihr habt glaube ich noch nicht ganz verstanden was ich vorhabe :lol: deshalb habs ichs auch eigentlich dazugeschrieben, das war ja nur testcode, so verwende ich das ganze:

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:
unit UMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DXDraws, DXClass, StdCtrls, math;

const
  Spark_Amount = 500;
  Radius = 150;

var
  Spark_Life: integer = 50;
  MouseControl: boolean = false;
  gPosi: integer = 0;

type
  TTarget = record
    x, y: real;
  end;

type
  TSpark = record
    x, y, sx, sy: real;
    age: integer;
  end;

type
  TForm1 = class(TForm)
    DXDraw1: TDXDraw;
    DXImageList1: TDXImageList;
    DXTimer1: TDXTimer;
    procedure FormCreate(Sender: TObject);
    procedure DXTimer1Timer(Sender: TObject; LagCount: Integer);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    Target: TTarget;
    Spark: array[0..Spark_Amount] of TSpark;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  with Target do
  begin
    x := Clientwidth div 2;
    y := Clientheight div 2;
  end;
  for i := 0 To Spark_Amount do
    with Spark[i] Do
    begin
      x := Target.x;
      y := Target.y;
      age := random(Spark_Life);
    end;
end;

procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
Var
  i: integer;
  r: TRect;
  str: string;
  fps, copyr: string;
Begin
  inc(gposi);
  if gposi = 180 then
    gposi := 0;
  Target.x := clientwidth div 2 + round(cos(degtorad((gposi * 2) - 90)) * radius);
  Target.y := clientheight div 2 + round(sin(degtorad((gposi * 2) - 90)) * radius);

  if not DXDraw1.CanDraw then Exit;
  DXDraw1.Surface.Fill(0);
  for i := 0 To Spark_Amount Do
    with Spark[i] do
    begin
      dec(age);
      x := x + sx;
      y := y + sy;
      if (age <= 0Or (x < 0Or (x + DXImageList1.items.find('Spark').height > width) Or (y < 0Or (y + DXImageList1.items.find('Spark').height > height) then
      begin
        x := Target.x;
        y := Target.y;
        sx := (random(10) - 5) / 5;
        sy := (random(10) - 5) / 5;
        age := random(Spark_Life);
      end;
      r := bounds(round(X), round(Y), DXImageList1.items.find('Spark').height, DXImageList1.items.find('Spark').height);
      DXImageList1.Items.Find('Spark').DrawAlpha(DXDraw1.Surface, R, 0, Spark[i].Age * 255 Div Spark_Life);
    end;
  str := 'Lebensdauer setzen mit + und -';
  copyr := 'Seth 2005';
  fps := 'FPS: ' + inttostr(DXTimer1.Framerate);
  with DXDraw1.Surface.Canvas do
  begin
    Font.Name := 'Arial';
    Font.Size := 12;
    TextOut(00, str);
    TextOut(0, clientheight - textheight(copyr), copyr);
    TextOut(clientwidth - textwidth(fps), 0, fps);
    Release;
  end;
  DXDraw1.flip;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if key = vk_add then
    if Spark_Life < 1000 then
      inc(Spark_Life);
  if key = vk_subtract then
    if Spark_Life > 1 then
      dec(Spark_Life);
end;

Initialization
  Randomize;

end.

der markierte code ist entscheident, trotzdem wären verbesserungsvorschläge net schlecht ;)


alzaimar - Fr 23.09.05 13:37

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
ihr habt nich wirklich geschnallt was ich vorhab :lol:

Du hast nicht wirklich gesagt, was Du vorhast :bawling:

Wenns läuft, dann is doch alles ok...
Verbesserungen:
Statt imm er dgposi := dgposi + 1 zu machen und dann in RAD umzurechnen, definier Dir doch einfach eine Konstante 'OneDegreeRAD = 2PI/360' oder was das ist und benutz das anstelle von '+1'. Dann sparst Du Dir schonmal das Umrechnen in rad.

Dann suchst Du 2x nach einem Image in der Imagelist... such sie nur 1x, das reicht.

Abschließend gehören solche Konstanten wie 180 etc. in den 'Const' Teil der Unit...

Letzte Verbesserung: Arbeite an Deiner Fähigkeit, Probleme zu artikulieren :mrgreen:


F34r0fTh3D4rk - Fr 23.09.05 16:22
Titel: Re: Einen Kreis pixelweise zeichnen -> Spline
user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
wenns fertig ist, soll es ne art spline werden, an dem dann ein objekt langwandert, vielleicht das wollte ich dann für jede iteration berechnen, halt so wie im mini beispiel oben. da stellt sich mir auch noch die frage wie ich das dann mit der geschwindigkeit mache, hat da jemand ahnung von ?


delfiphan - Fr 23.09.05 16:37

Geh ich richtig in der Annahme, dass du jetzt eine funktionierende Lösung hast? (Wieso posten alle weiter?)


F34r0fTh3D4rk - Fr 23.09.05 16:44

der beitrag ist auch schon als beantwortet markiert, schon längst :lol:


alzaimar - Fr 23.09.05 16:45

Touché :roll: , "so ne Art Spline".... Na nun, bleiben wir also bei einem Kreis, den ein Spline ist dann wieder etwas ganz Anderes. Du solltest Dir zuerst also überlegen, in welcher Schrittweite dein Ding rumwandern soll (von pixel zu pixel, oder wie). Dann erzeugst Du Dir den Pfad dazu als Array, z.B. Für pixelgenaue Bewegungen würde ich trotzdem den Bresenham nehmen. Der erzeugt zwar nur eine 8tel Kreis, aber, das kann man einfach spiegeln (und ein wenig rechnen). Also: Du erzeugst Dir ein X/Y-Array und wanderst einfach jeden Punkt in dem Array an.

Also, so würd' ich das wenigstens machen.


F34r0fTh3D4rk - So 25.09.05 15:03

user profile iconalzaimar hat folgendes geschrieben:
Touché :roll: , "so ne Art Spline".... Na nun, bleiben wir also bei einem Kreis, den ein Spline ist dann wieder etwas ganz Anderes. Du solltest Dir zuerst also überlegen, in welcher Schrittweite dein Ding rumwandern soll (von pixel zu pixel, oder wie). Dann erzeugst Du Dir den Pfad dazu als Array, z.B. Für pixelgenaue Bewegungen würde ich trotzdem den Bresenham nehmen. Der erzeugt zwar nur eine 8tel Kreis, aber, das kann man einfach spiegeln (und ein wenig rechnen). Also: Du erzeugst Dir ein X/Y-Array und wanderst einfach jeden Punkt in dem Array an.

Also, so würd' ich das wenigstens machen.

nee, splines sind in der regel vektorgrafiken, bzw vektorpfade. und dazu gehört auch ein kreis.


delfiphan - So 25.09.05 15:17

Splines sind nicht einfach irgendwelche Vektorpfade. Es gibt viele verschiedene Splines. Spricht man aber von "Spline" ist häufig der "natürliche, kubische Spline" gemeint. Wenn du einen Pfad darstellen willst, brauchst du einen parametrischen Spline.

Aber egal welche Spline-Sorte: Soweit ich weiss, kann man keinen Kreis exakt durch Splines darstellen. Näherungsweise natürlich schon.


alzaimar - Mo 26.09.05 21:08

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
nee, splines sind in der regel vektorgrafiken, bzw vektorpfade. und dazu gehört auch ein kreis.

Wie delfiphan schon sagte, splines hat nix mit kreisen zu tun. Wat is also ne spline?
Nimm Dir mal N (n>3, glaub ich) Punkte (X,Y), wobei zunächst Xi > X(i-1). Nun willst Du die durch eine glatte Kurve verbinden. Das kann man natürlich auf vielfältige Art und Weise machen. Eine Möglichkeit ist die, zwischen je zwei Punkten ein Polynom 3.Grades zu zeichnen. Zwischen P1 und P2 ist dann das Polynom C1, zwischen P" und P3 ist das Polynom C2 usw. Jedes Poynom ist durch 4 Parameter (a,b,c,d) gekennzeichnet. Nun ist nur noch zu klären, wie man die N-1 Polynome bekommt, sodass diese 'glatt' nebeneinander liegen. Klar, die erste Ableitung am Endpunkt von C1 muss = der 1.Ableitung am Anfangspunkt von C2 sein usw. Dann nur noch dafür sorgen, das die Y-Werte der Polynome gelten und schon hat ein Gleichungssystem, das man lösen kann. Man braucht nur noch die Anfangssteigungen bzw. die 2.Ableitung der Anfangs- und Endpunkte (P1 und Pn) vorgeben. Dann setzt man einen Gauss drauf an, bekommt für jedes Polynom die Coeffizienten und fertig.

Ausgleichsplines (Bezier-Kurven) gibt es auch noch. Dabei geht die Kurve nicht durch die Punkte, sondern glättet den Pfad zwischen den Punkten ("Ausgleichskurve").

Im Metafont-Programm von D.Knuth werden Bernsteinpolynome verwendet, das sie sehr leicht zu berechnen sind.

So viel zu dem 'Nee'.