Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   ¿Cómo pintar un círculo matemáticamente? (https://www.clubdelphi.com/foros/showthread.php?t=62594)

aeff 03-01-2009 16:33:52

¿Cómo pintar un círculo matemáticamente?
 
saludos!

miren colegas, el problema es que necesito pintar un círculo matemáticamente, sin usar el método Ellipse del Canvas ya que con este método si el Pen.Width = 1 e intento ir "cerrando el radio" y repintar un nuevo círculo quedan pixeles sin pintar, mi objetivo es lograr un Degradado Radial parecido al de PhotoShop, pero lo que necesito primeramente es logarar que no ocurra lo que acabo de explicar, si uso la fórmula matemática del círculo: R^2 = (X - H)^2 + (Y - K)^2, y despejo para halla a que valor de X le corresponde en Y según el radio, me sucede lo siguiente:

Código Delphi [-]
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
  R: Real;
implementation
{$R *.DFM}
uses Math;
  function GetY(R, X: Real ): Real;
  var
    H, K, solve1, solve2: real;
  begin
    // R^2 = (X - H)^2 + (Y - K)^2
    H := R;
    K := R;
    solve1 := sqr(R) - sqr ( X - H );
    solve2 := (sqrt( abs(solve1) )) + k;
    Result := solve2;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  X: Real;
  Y: Integer;
  Value: Byte;
begin
  X := 0.000;
  Screen.Cursor := crHourGlass;
  while X <= R * 2 do
    begin
      Y := Round(GetY(R, X)) ;
      Value := 255 * Round(Y * (sin(DegToRad(Y))));
      Canvas.Pixels[ 200  - Round(R) + Round(X), 200 - round(R) + Y ] := RGB(Value, Value, Value);
      Canvas.Pixels[ 200  - Round(R) + Round(X), 200 + round(R) - Y  ] := RGB(Value, Value, Value);
      X := X + 0.1;
    end;
  Screen.Cursor := crDefault
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  R := 100;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 1 to 100 do
    begin
      R := I;
      Button1Click(self);
    end;
end;

nuevamente existen pixeles que no lograr cerrar el arco completamente,

espero que me hallan entendido y que me puedan responder.

mil gracias de antemano,
saludos!
aeff!

coso 03-01-2009 18:21:48

hola, creo que te has hecho un lio. Te dejo el codigo, y te recomiendo que lo entiendas

Código Delphi [-]
procedure TForm1.Button1Click(Sender: TObject);
var
     x,y,r,i,j : integer;
begin
     x := 150;
     y := 150;
     r := 50;
     for i := x-r to x+r do
     for j := y-r to y+r do
          if ((i-x)*(i-x) + (j-y)*(j-y)) < r*r then canvas.pixels[i,j] := (j+i)*3;

end;

procedure TForm1.Button2Click(Sender: TObject);
var
     x,y,r,i,j : integer;
     phi       : double;
begin
     x := 150;
     y := 350;
     r := 50;
     phi := 0;
     while phi < 2*PI do
     begin
          i := Round(x + r*cos(phi));
          j := Round(y + r*sin(phi));
          canvas.pixels[i,j] := i+j;
          phi := phi + 0.001;
     end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
     x,y,r,i,j : integer;
     rd : double;
     cl : array of TColor;
begin
     x := 150;
     y := 150;
     r := 50;

     setlength(cl,r);

     for i := 0 to length(cl) - 1 do
          cl[i] := Round(255-i*255/r)*$010000 ;

     for i := x-r to x+r do
     for j := y-r to y+r do
     begin
          rd := (i-x)*(i-x) + (j-y)*(j-y);
          if rd < r*r then canvas.pixels[i,j] := cl[Round(sqrt(rd))];
     end;

     setlength(cl,0);
end;

aeff 03-01-2009 22:18:22

saludos, bueno, ¿me puedes explicar el ejemplo del button 2?, ¿por que es seno y coseno? si en la fórmula del círculo no veo esto,

** nota: no tengo conocimientos fuertes de matemática, por esto es que paso tanto trabajo**

mil gracias de antemano colega!

saludos!
aeff!

coso 03-01-2009 23:10:24

1 Archivos Adjunto(s)
A ver...es trigonometria: en un triangulo rectangulo (con un angulo de 90 grados o PI/2 radiantes)...

sino de angulo (phi) = lado opuesto (y) / hipotenusa (r)=> y = r*sin(phi) . Para desplazarlo al centro que tu quieras, y = cy + r*sin(phi)

cosino de angulo (phi) = lado adyacente (x) / hipotenusa (r) => x = r * cos(phi). Para desplazarlo al centro, x = cx + r*sin(phi)

para dibujar el circulo, entonces, vas encontrando las coordenadas x,y manteniendo fija la r, recorriendo 2*PI radianes (360º) "fabricando" los triangulos rectangulos

ya que estamos, prueba :

Código Delphi [-]
procedure TForm1.Button2Click(Sender: TObject);
var
     x,y,i,j : integer;
     phi       : double;
begin
     x := 150;
     y := 350;
     phi := 0;
     while phi < 1000 do
     begin
          i := Round(x + phi*cos(phi));
          j := Round(y + phi*sin(phi));
          canvas.pixels[i,j] := i+j;
          phi := phi + 0.001;
     end;
end;
;)

Robert01 03-01-2009 23:13:05

Hola

Yo te explico, disculpame que me meta.

x + r*cos(phi)) esto es el valor de X más la proyección de r sobre ese mismo eje de coordenadas,
y + r*sin(phi)) es el valor de y más el valor de r proyectado sobre el otro eje.

En resumen, te da las coordenadas de un punto (i,j), son valores enteros por eso usa trunc para tomar solamente la parte entera de las ecuaciones anteriores.

Está usando coordenadas polares me parece si mal no recuerdo

Mirá aquí

Saludos

Disculpame coso

coso 03-01-2009 23:17:51

Cita:

Está usando coordenadas polares me parece si mal no recuerdo
si, pasando polares a cartesianas o rectangulares

Cita:

Disculpame coso
:eek: pq? si no lo he inventado yo ;) y lo que has dicho es cierto . Saludos de nuevo

aeff 04-01-2009 01:29:13

ñoooooo! ahora si que veo lo linda que es la matemática, nunca me inmaginé como aplicar de esta forma las razones trigonometricas seno y coseno, mil gracias a todos por su ayuda,

por último, ¿como puedo hacer este círculo con relleno y que no se demore? porque usando este método de hallar lado opuesto y lado adyacente hacemos un recorrido hasta 360º de 0.001 en 0.001, y esto provoca que si voy disminuyendo el radio y vuelvo a pintar el círculo de esta forma nuevamente se producirá el ciclo hasta 360º , inmagínense que haga un Degradado Radial con un círculo que tenga como radio 800px, ¿cuanto tardará???..

espero que puedan ayudar nuevamente,

mil gracias de antemano!
AEff!

duilioisola 04-01-2009 14:48:39

Lo que puedes hacer es pensarlo al reves...
Tienes un cirulo de 800px de diámetro. Por lo tanto entra dentro de un cuadrado de 800px con centro en (x,y).
Lo que puede hacer es ir leyendo cada pixel y ver si está dentro del círuculo.
- averiguas el valor del radio para (x,y).
--- para esto utilizas r=sqrt(x²+y²).
- si el radio es menor o igual a 400, está dentro del círculo.
--- luego pudes decidir qué color le corresponde según el valor del radio.
--- (radio(1)= rojo puro, radio(400)=azul puro, radio(200)= 50%rojo+50%azul, ...)
- si es mayor, lo descartas.

De esta manera la cantidad de iteraciones es 800x800=64000 (y solo en algunas se pinta)

Delphius 04-01-2009 18:30:41

Como recomendación extra, sería útil que en vez de usar Pixels[] emplearas ScanLine(). El acceso es mucho más rápido.

Tal vez sea un poquito más complicado y requiera unas cuantas lineas más de código, pero se gana mucho en perfomance.
En otras ocasiones se habló de scanline, e incluso hay código con ejemplos de uso. Recomiendo una búsqueda con dicho término.

Saludos,

aeff 06-01-2009 13:52:37

miren, la solución que encontré, ¿que opinan?, para no calentarme mucho la cabeza:

Código Delphi [-]
  type TColorPercent = record
    Color: TColor;
    Percent: 0..100;
  end;

  procedure Circle(ACanvas: TCanvas; X, Y, Radio: Integer);
  var
    pLeft, pTop, pDimX, pDimY: Integer;
  begin
    with aCanvas do
      begin
        Brush.Color := Pen.Color;
        pLeft := X - Radio;
        pTop  := Y - Radio;
        pDimX := X + Radio;
        pDimY := Y + Radio;
        Ellipse(pLeft, pTop, pDimX, pDimY);
      end;
  end;

  function GetChannelIn(X1, X2: Byte; Index, Max: LongInt): Byte;
  var
    vPercentIndex, vDifference, vPercentDiff: Real;
  begin
    vPercentIndex := 0.0;
    try
    // p/t = x/100
    // Index/max = x/100
    // index/max * 100 = x
      if Max = 0 then Max := 1; 
      vPercentIndex := Index / Max * 100;
    except

    end;
      vDifference := X2 - X1;
    // p/t  = x/100
    // p/vDifference = vPercentIndex/100
    // vPercentDiff = vPercentIndex / 100 *  vDifference;
      vPercentDiff := vPercentIndex / 100 *  vDifference;
      Result := X1 + Round(vPercentDiff);
  end;

  {-«------------------------------------------------------------------------»-}

  function GetGradientValue(aColor1, aColor2: TColor; Index, Max: Longint): TColor;
  var
    Color1, Color2: Integer;
    R1, G1, B1, R2, G2, B2, nR, nG, nB: Byte;
  begin
    Color1 := ColorToRGB(aColor1);
    Color2 := ColorToRGB(aColor2);
    R1 := GetRValue(Color1);  G1 := GetGValue(Color1);  B1 := GetBValue(Color1);
    R2 := GetRValue(Color2);  G2 := GetGValue(Color2);  B2 := GetBValue(Color2);

    nR := GetChannelIn(R1, R2, Index, Max);
    nG := GetChannelIn(G1, G2, Index, Max);
    nB := GetChannelIn(B1, B2, Index, Max);

    Result := TColor(RGB(nR, nG, nB));
  end;

 procedure GetRadialFigure(aWidth, aHeight: Integer; aColorList: array of TColorPercent;
                                             var vResult: TBitmap);
  var
    aBitmap: TBitmap;
    aRadio, aSubRadio, CX{Centro X}, CY{Centro Y},
    Index, xFrom, xTo, xRect: Integer;
    aColor1, aColor2, I: TColor;
  begin

    if High(aColorList) <= 0 then Exit;
    aBitmap := TBitmap.Create;
    aBitmap.Assign(vResult);
    aBitmap.PixelFormat := pf24bit;
    aBitmap.Width := aWidth;
    aBitmap.Height := aHeight;
    aRadio := Round(Sqrt( Sqr(aWidth) + Sqr(aHeight) ) / 2);
    CX := aWidth div 2;
    CY := aHeight div 2;

    Index := High(aColorList);


    while Index > 0 do
      begin
        xFrom := (aColorList[Index -1].Percent);
        xTo := (aColorList[Index].Percent);
        xFrom := Round(xFrom / 100 * aRadio);
        xTo := Round(xTo / 100 * aRadio);
        aSubRadio := xTo;  {Índice, o nuevo radio, o radio drecementativo}
        xRect := xTo - xFrom;  {Valor máximo}

        aColor1 := aColorList[Index -1].Color; {Menor}
        aColor2 := aColorList[Index].Color;  {Mayor}
        I := xRect;

        while aSubRadio > xFrom do
          begin
            aBitmap.Canvas.Pen.Color := GetGradientValue(aColor1, aColor2, I, xRect);
            Circle(aBitmap.Canvas, CX, CY, aSubRadio);
            aSubRadio := aSubRadio - 1;
            Dec(I);
          end;
        Dec(Index);
      end;

    vResult.Assign(aBitmap);
    aBitmap.Free;
  end;

ahora, añado un TImage y un Button y en el evento OnCLick del button coloco esto:

Código Delphi [-]
procedure TForm1.Button1Click(Sender: TObject);
var
  aBmp: TBitmap;
  lstColors: array of TColorPercent;
begin
       //
  SetLength(lstColors, 2);
  lstColors[0].Percent := 0; lstColors[0].Color := clRed;
  lstColors[1].Percent := 100; lstColors[1].Color := clYellow;
  aBmp := TBitmap.Create;
  aBmp.Width := 200;
  aBmp.Height := 200;
  aBmp.PixelFormat := pf24bit;
  GetRadialFigure(200,200, lstColors, aBmp);
  Image1.Picture.Assign(aBmp);
end;

¿bueno, que opinan?

saludos!
aeff!

coso 06-01-2009 17:16:24

hombre, personalmente lo haria de otra manera, segun para que quiero utilizarlo, pero si a ti te va bien, perfecto ;)

MAXIUM 12-01-2009 01:06:05

Usando el algoritmo de Bresenham's
http://sophia.javeriana.edu.co/~cbus...imitivas2D.pdf


La franja horaria es GMT +2. Ahora son las 10:20:57.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi