如何画斜椭圆?(不用画点的方法)(88分)

  • 主题发起人 主题发起人 于小澜
  • 开始时间 开始时间

于小澜

Unregistered / Unconfirmed
GUEST, unregistred user!
我有一个画斜椭圆的方法,但是是用画点的
方法实现的,速度慢:
procedure TForm1.Button1Click(Sender: TObject);
var
angle: Integer;
iX, iY: Integer;
iShort, iLength: Integer;
begin
for angle:=0 to 359 do
begin
iX := Trunc(36*Sin(angle));
iY := Trunc(64*Cos(angle));
iShort := Round(iX*Cos(60) - iY*sin(60))+200;
iLength := Round(iX*Sin(60) + iY*Cos(60))+200;
Canvas.Pixels[iShort, iLength] := Rgb(255, 0, 0);
end;
end;
我想用类似画矩形的方法(Rectangle)、椭圆(ellipse)来实
现,主要是对速度要求高。请指教!
 
GDI:Region & Path
otherwise:OpenGL
 
其实它们的画法也都是点,但可能有优化,

to xiangya:
Region 用什么来弄呢? 只能画出标准的,Path 也一样。

 
我想看看API中ellipse如何实现,有谁知道?
 
这个不会有的,都在 dll 里,
delphi 的源码里可能有,不过算法看看 图形学的书就可以了,
 
SuperMMX:
Delphi中我找了没有代码。
各位谁有ellipse(Api)的原代码?
 
想增加速度有两个办法:
一是检测每次生成的点是否覆盖了已经画了的点,如果已经画了就不要再画了.
二是把Sin,Cos的360个角度的数值预先算好,存放到数组里面.

可以参考一下直线,圆弧的DDA算法.
 
把你的程序优化一下,如把cos(60)、sin(60)提出循环;再用汇编,速度会提高不少。(再看看Sin(angle)有没有什么快速算法,如用累加法等)
 
你要画的椭圆是只要求形象,还是要求精度很高,如果要求不太高的话可以这样考虑:一个椭圆近似地可以看成由四段圆弧组成,只要将这四段圆弧旋转就可以了,实际上用这种方法画出的椭圆应该是相当好的,而且速度也很快,你可以试一下
 
不用画点的方法自然就是画线段了,比如说把360度角度每10度用一个线段,这样用36
条线段在屏幕上看起来还是比较平滑的
 
唉,大家挥刀一通乱砍,竟然没有一个人提到bresenham,真让我失望.

任何实用的弧线绘制都是用bresenham方法,WinAPI当然也不例外,
只要使用这个方法绘制ellipse,再加上旋转偏移量就可以了.

(源码如下,我用了一会儿从C程序改过来的,所以很多变量名字都无意义,
旋转偏移量那部分是我加的.)

procedure tform1.DrawRotateEllipse(center:tpoint;long,short:integer;RASin,RACos:real);
procedure symmetry(x,y:integer);
var
tx,ty:integer;
begin
tx:=round(x*RACos-y*RASin);
ty:=round(x*RASin+y*RACos);
Canvas.Pixels[Center.x+tx,Center.y+ty] := Rgb(255, 0, 0);
tx:=round(-x*RACos-y*RASin);
ty:=round(-x*RASin+y*RACos);
Canvas.Pixels[Center.x+tx,Center.y+ty] := Rgb(255, 0, 0);
tx:=round(-x*RACos-(-y)*RASin);
ty:=round(-x*RASin+(-y)*RACos);
Canvas.Pixels[Center.x+tx,Center.y+ty] := Rgb(255, 0, 0);
tx:=round(x*RACos-(-y)*RASin);
ty:=round(x*RASin+(-y)*RACos);
Canvas.Pixels[Center.x+tx,Center.y+ty] := Rgb(255, 0, 0);
end;
var
x,y,a2,b2, S, T:integer;
a,b:integer;
begin
a:=long;
b:=short;
a2 := a*a;
b2 := b*b;
x := 0;
y := b;
S := a2*(1-2*b) + 2*b2;
T := b2 - 2*a2*(2*b-1);

symmetry(x,y);
while (y>=0) do
begin
if (S<0) then
begin
S := S+2*b2*(2*x+3);
T := T+4*b2*(x+1);
x:=x+1;
end
else if (T<0) then
begin
S :=S+ 2*b2*(2*x+3) - 4*a2*(y-1);
T := T+4*b2*(x+1) - 2*a2*(2*y-3);
x:=x+1;
y:=y-1;
end
else
begin
S := S-4*a2*(y-1);
T := T-2*a2*(2*y-3);
y:=y-1;
end;
symmetry(x,y);
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
cent :tpoint;
begin
cent.x:=200;
cent.y:=100;
DrawRotateEllipse(cent,64,36,sin(pi/6),cos(pi/6));
end;

顺便说两个问题,

1.我这个旋转算法是按你那个例子里写的,发现是逆时针呀! :-)
2.sin,cos参数是弧度值,因此不能用30,60,而应用pi
 
有没有结果呀?
 
温柔一刀真厉害,不过画出来的椭圆显得很粗糙,看上去不光滑,请问有没有好的办法改进?
 
procedure TForm1.Button1Click(Sender: TObject);
var
angle: Integer;
iX, iY, c, s: Double;
iShort, iLength: Integer;
begin
c := Cos(Pi / 3);
s := Sin(Pi / 3);
Canvas.Pen.Color := clRed;
for angle:=0 to 72 do
begin
iX := 36 * Sin(angle * Pi / 36);
iY := 64 * Cos(angle * Pi / 36);
iShort := Round(iX * c - iY * s) + 200;
iLength := Round(iX * s + iY * c) + 200;
if angle = 0 then Canvas.MoveTo(iShort, iLength)
else Canvas.LineTo(iShort, iLength);
end;
end;

用画点的方法当然慢,上面的程序是用画线的方法的。

如果想再提高速度,可以使用双缓冲机制,相信效果会很明显!
 
有没有想过使用多边形逼近椭圆的方法呢?
这个方法就是一开始把椭圆逼近成一个多边形(边数最多200就可以非常平滑了)
然后只要调用api的polygone就可以了

这个方法主要浪费时间的地方在于第一次把那些点的坐标装到点的数组中去
而画图是几乎没什么时间消耗的。。因为处理一个多边形比处理一个椭圆容易的多

breshman算法是个好算法,不过如果知道了该斜椭圆的方程(隐式的)可以使用
正负算法,是个好方法。不过这两个都是要用画点的,而delphi的那个pixels,
实在是不用也罢。太慢了!

所以我推荐你使用多边形逼近椭圆的算法,好处有两个
第一,可以保证得到的椭圆绝对是很光滑的。
第二,这个方法虽然第一次装入的时候要花点时间,不过只是相对多,事实上也没有多少
而把那些点装入数组后,你如果想要画出该椭圆倾斜其他角度的形状时,就可以
一劳永易了。而且最重要的是,直接调用了画多边形的api,比画线和画点都快多了

如果你还是不满意的话。。。我想你还是使用汇编直接去写端口吧。。。
这个肯定快。:)
 
说来说去,还不都 是以点或直线来实现吧
 
说来说去你们就没想道这样的问题完全可以用Windows API实现吗?
记得API有实现图形旋转的函数,好像是设置一个旋转矩阵,具体的去查一下,
当然,自己也完全可以做,可以用bresenham算法,我没仔细看上面的代码,但似乎
不应该调用太多的三角函数,否则的话就不是bresenham了,bresenham速度快主要就是
因为没有使用三角函数。
 
画个椭圆,然后旋转画布不就完了么?!
 
SetWorldTransForm肯定是可以做的,但是我只在NT和2000下试了,9x没试,有可能不行,
另外,用SetWorldTransForm的话,坐标算起来很麻烦,我觉得还是用分段直线代替更简便,
先生成坐标点,然后用PolyLine即可一次画出(注意:PolyLine需将起始点加入两次),
我用36等分效果就已经很好了。
 
>>不过画出来的椭圆显得很粗糙,看上去不光滑

肯定是我的程序改造时候循环不完全正确导致的,
C改pascal时候循环比较麻烦 :-)
 
后退
顶部