圆内排列随机个正方形

时间:2022-11-08 16:05:28
圆内排列随机个正方形
圆大小固定,里面正方形个数随机,求个排列的办法,使图形尽量美观

30 个解决方案

#1


  你这是彩票类程序?帮你顶一下。

#2


引用 1 楼 ecjtu5208 的回复:
你这是彩票类程序?帮你顶一下。

为什么这么说呢?

#3


各位高手,来教教我啊

#4


刚不给你回了么,你要的不是这个效果么?

可以这样设计算法,不过效率可能比较低,假设圆的直径是d,小正方形的个数是n,按你说的这个n是指定的,可以是任何一个数量,小正方形的边长是L,,但如果是正方形的话不能保证边缘的正方形一定和圆相交,下面是伪代码


L初始为0
while(TRUE)//循环
{
L+=1 每次循环边长+1
//用L长的小正方形去填充满边长为d大正方形,那个圆就是这个大正方形的内切圆,假设用了x个小正方形
x=d^2/L^2 //x取整
t=x
for(i=0;i<t;i++) //循环x次
{
检测第i个小正方形的4个顶点是不是在直径为d的内切圆内,是-继续,不是-则x-1
}
比较x和n,当x第一次小于n的时候,中断循环
}
L值即为在n个小正方形填充圆情况下的小正方形边长



不知道楼主是不是这个意思,还是我理解错了。

#5


引用 4 楼 bluekitty 的回复:
刚不给你回了么,你要的不是这个效果么?

可以这样设计算法,不过效率可能比较低,假设圆的直径是d,小正方形的个数是n,按你说的这个n是指定的,可以是任何一个数量,小正方形的边长是L,,但如果是正方形的话不能保证边缘的正方形一定和圆相交,下面是伪代码


Delphi(Pascal) code

L初始为0
while(TRUE)//循环
{
L+=1 每次循环边长+1
//用……

是这个意思,可圆和小正方形都是画线画上去的,怎么判断顶点是不是在园内啊

#6


你画小正方形也得要4个顶点坐标才能画啊,判断一个点是不是在一个圆内,这个。。。。。,这个咱们初中就学过-_-!,这点到圆心的距离小于半径就在园内,大于就在圆外,等于就在圆上,还是给你写个例子

假如一个顶点在v(x,y),圆心在c(x',y')

s=sqrt((x-x')^2+(y-y')^2); //勾股定理,s是到圆心的距离
s<D/2就在园内 //D是直径,所以除以2

#7


正方形大小是否统一?位置是否可以重叠?

#8


引用 7 楼 sz_haitao 的回复:
正方形大小是否统一?位置是否可以重叠?

大小一样,位置不能重叠,必须一个挨一个

#9


那就不算随机了

假设小正方形边长为x
则有x*x种(或更少)排法
全部试一遍就行了

#10


引用 9 楼 sz_haitao 的回复:
那就不算随机了

假设小正方形边长为x
则有x*x种(或更少)排法
全部试一遍就行了

数量是随机的,大小也会随数量缩小,不过全部的正方形都是一样大。
你能将排法说的详细一些吗?

#11


假设小正方形边长为a,就是以园的最左点x坐标m和最上点y坐标n为起始点(m,n),
画出横线(x=m)和竖线(y=n),再移a像素划出横线(x=m+a)和竖线(y=n+a),。。。。它们构成一种正方形的组合
然后以(m+1,n+1)为起始点画出横线(x=m+1)和竖线(y=n+1),再移a像素划出横线(x=m+1+a)和竖线(y=n+1+a),。。。。它们构成第2种正方形的组合
。。。

#12


引用 11 楼 sz_haitao 的回复:
假设小正方形边长为a,就是以园的最左点x坐标m和最上点y坐标n为起始点(m,n),
画出横线(x=m)和竖线(y=n),再移a像素划出横线(x=m+a)和竖线(y=n+a),。。。。它们构成一种正方形的组合
然后以(m+1,n+1)为起始点画出横线(x=m+1)和竖线(y=n+1),再移a像素划出横线(x=m+1+a)和竖线(y=n+1+a),。。。。它们构成第2种正方形的组合
。。。

我请教个问题,delphi能不能先画个网状的图形,然后画个圆,最后只现在圆里圈上的小方格呢

#13


可以啊,循环画横线和竖线,间隔都是a
然后画园
圆心的坐标也是a*a种选择

#14


先说说画这个干嘛。。

#15


引用 13 楼 sz_haitao 的回复:
可以啊,循环画横线和竖线,间隔都是a
然后画园
圆心的坐标也是a*a种选择

能给个简单例子吗?我不太熟悉delphi

#16


引用 14 楼 vividw 的回复:
先说说画这个干嘛。。

涉及保密问题,我不能说啊大哥,这是我的任务,明天交活了,可我现在还没做完,所以到这里请各位高手指教啊。

#17


因为是整形的 算法肯定 不是严格相切的,
这个条件不是严格的 求解是仅仅是美观 。。
 画出来就行了。。

#18


引用 17 楼 vividw 的回复:
因为是整形的 算法肯定 不是严格相切的,
这个条件不是严格的 求解是仅仅是美观 。。
 画出来就行了。。

我现在都没明白咋做呢 我没用过delphi画图 请大哥详细点说啊,最好能给点简单的代码,我也好参考

#19


你用delphi做过什么?
还是用熟悉的 语言写吧。。

#20


引用 19 楼 vividw 的回复:
你用delphi做过什么?
还是用熟悉的 语言写吧。。

要是让用别的语言我早用了。。。只能用delphi做

#21


引用 19 楼 vividw 的回复:
你用delphi做过什么?
还是用熟悉的 语言写吧。。

我第一次用delphi

#22


用穷举法吧。

#23


引用 22 楼 lhylhy 的回复:
用穷举法吧。

高手 ,不要说名词,我不明白,给个简单的例子呗

#24


-_-!,你这样难度太大了,从来没用过delphi怎么写啊,吃晚饭我给你写个吧,就用我那个算法,很简单的

#25


下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:
圆内排列随机个正方形

源代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    PaintBox1: TPaintBox;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure PaintBox1Paint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;



var
  Form1: TForm1;
  Vertexes:array of TPoint;  //存储顶点
  CurL:integer;   //当前的小正方形边长
  DrawState:boolean=false;  //是否可以在OnPaint事件里面绘图

implementation

{$R *.dfm}

procedure AddVertex(vt:TPoint);
begin
  SetLength(Vertexes,Length(Vertexes)+1);
  Vertexes[Length(Vertexes)-1]:=vt;
end;

function RectInPie(vTopLeft,COC:TPoint;CurL,R:integer):boolean;
begin
  Result:=(sqrt(sqr((vTopLeft.X - COC.X))+sqr((vTopLeft.Y - COC.Y)))<=R)  and
          (sqrt(sqr((vTopLeft.X+CurL - COC.X))+sqr((vTopLeft.Y - COC.Y)))<=R) and
          (sqrt(sqr((vTopLeft.X+CurL - COC.X))+sqr((vTopLeft.Y+CurL - COC.Y)))<=R) and
          (sqrt(sqr((vTopLeft.X - COC.X))+sqr((vTopLeft.Y+CurL - COC.Y)))<=R);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
  rClient:TRect;
  i:integer;
  pt:TPoint;
begin
  rClient:=PaintBox1.ClientRect  ;
  with PaintBox1.Canvas do begin
     Brush.Color:=clBlack;
     Brush.Style:=bsSolid;
     Rectangle(0,0,rClient.Right,rClient.Bottom);
     //以上代码把PaintBox1的背景涂成黑色

     //画圆
     Pen.Color:=clYellow;
     Ellipse(0,0,rClient.Right,rClient.Bottom);

     if DrawState then begin //绘制小正方形
       Pen.Color:=clBlue;     //边框蓝色
       Brush.Color:=clLime;   //填充亮绿色
       for i:=0 to Length(Vertexes) -1 do begin
          pt:=Vertexes[i];
          Rectangle(pt.X,pt.Y,pt.X+CurL,pt.Y+CurL);
       end;
     end;
  end;//end with
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  L,n,r,x:integer;
  nCount,nCountPerLine,nLine,i:integer;
  V:TPoint;    //左上角顶点
begin
  if not TryStrToInt(Edit1.Text,n) then exit; //指定用多少个小正方形
  DrawState:=false; //暂时不允许绘图
  r:=PaintBox1.ClientRect.Right div 2;       //圆半径
  L:=3;      //小正方形边长

  while true do begin

    Inc(L); //从4开始,太小没意思
    SetLength(Vertexes,0); //清空顶点
    x:=sqr(2*r) div sqr(L);   //求要用多少个小正方形填充大正方形
    nCount:=x;
    nCountPerLine:=Round(2*r/L); //一行有多少个小正方形
    nLine:=0;  //从第一行开始

    for i:=1 to x do begin

      if i mod nCountPerLine=0 then Inc(nLine);//换行

      //求小正方形的左上角坐标
      V.X:=L*(i mod nCountPerLine);
      V.Y:=nLine*L;

      if RectInPie(V,Point(r,r),L,r) then
        AddVertex(V)
      else
        Dec(nCount);

    end;  //end for

    if nCount<n then begin
      CurL:=L;    //求得小正方形边长,并终止循环
      Label2.Caption:=Format('实际用了 %d 个正方形(边长%d)',[nCount,CurL]);
      break;
    end;

  end; //end while

  DrawState:=true;
  PaintBox1.Invalidate;
  
end;

end.

#26


引用 25 楼 bluekitty 的回复:
下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:


源代码:
Delphi(Pascal) code

……

多谢大哥

#27


引用 25 楼 bluekitty 的回复:
下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:


源代码:
Delphi(Pascal) code

……

大哥,数量少了是什么原因,我应该改什么地方能让数量接近些

#28


你是用数量去推导边长,而且要填满一个圆形,这样不太正确,因为实际上边长是可以确定的,而数量不能确定,原因就是由于屏幕坐标都是整数,无法精确的绘制图形,边长的变化和数量的变化是离散的,可能边长增加或减少一点,数量不变,也可能边长每次增加10而每次减少的数量都不一样,即假如指定100个等大的正方形填满一个圆,在GDI模式实际上可能根本做不到,除非不填满。下面是优化过的代码,你可以再试一下,这样图形是上下左右对称的,好看而且效率高,不过数量更不准确

procedure TForm1.Button1Click(Sender: TObject);
var
  L,n,r:integer;
  nCount,nCountPerLine,nLine,i,t:integer;
  V:TPoint;
begin
  if not TryStrToInt(Edit1.Text,n) then exit; //指定用多少个小正方形
  ASSERT(n mod 4=0,'数目应该为4的倍数');
  DrawState:=false; //暂时不允许绘图
  r:=PaintBox1.ClientRect.Right div 2;       //圆半径
  L:=3;      //小正方形边长

  while true do begin  //求左上1/4圆周那个扇形可以容纳多少个边长为L的小正方形

    Inc(L);
    SetLength(Vertexes,0); //清空顶点

    nLine:=r div L;//一共nLine行

    for i:=1 to nLine do begin
      //计算第i行可以容纳多少个边长为L的小正方形
      nCountPerLine:=Trunc(sqrt(sqr(r)-sqr(i*L))/L); //截断,不要四舍五入
      for t:=1 to nCountPerLine do begin
        V.X:=r-t*L;
        V.Y:=r-i*L;
        AddVertex(V);
      end;//end 2 for

    end; //end 1 for

    nCount:=Length(Vertexes);

    ASSERT(nCount>0);

    if nCount<=n div 4 then begin
      CurL:=L;
      break;
    end

  end;//end 1 while



  for i:=0 to Length(Vertexes)-1 do  //右上侧
    AddVertex(Point(2*r-Vertexes[i].X-CurL,Vertexes[i].Y)); //横坐标和垂直半径对称

  for i:=0 to Length(Vertexes)-1 do  //上半圆整个沿水平直径再翻一次
     AddVertex(Point(Vertexes[i].X,2*r-Vertexes[i].Y-CurL));

  Label2.Caption:=Format('实际用了 %d 个小正方形(边长%d)',[Length(Vertexes),CurL]);
  DrawState:=true;
  PaintBox1.Invalidate;

end;

#29


有两种对称形状,一种是圆点是一个格子中间的,另一种是圆点是四个格子交界的。

#30


引用 28 楼 bluekitty 的回复:
你是用数量去推导边长,而且要填满一个圆形,这样不太正确,因为实际上边长是可以确定的,而数量不能确定,原因就是由于屏幕坐标都是整数,无法精确的绘制图形,边长的变化和数量的变化是离散的,可能边长增加或减少一点,数量不变,也可能边长每次增加10而每次减少的数量都不一样,即假如指定100个等大的正方形填满一个圆,在GDI模式实际上可能根本做不到,除非不填满。下面是优化过的代码,你可以再试一下,这样图形是……

多谢了

#1


  你这是彩票类程序?帮你顶一下。

#2


引用 1 楼 ecjtu5208 的回复:
你这是彩票类程序?帮你顶一下。

为什么这么说呢?

#3


各位高手,来教教我啊

#4


刚不给你回了么,你要的不是这个效果么?

可以这样设计算法,不过效率可能比较低,假设圆的直径是d,小正方形的个数是n,按你说的这个n是指定的,可以是任何一个数量,小正方形的边长是L,,但如果是正方形的话不能保证边缘的正方形一定和圆相交,下面是伪代码


L初始为0
while(TRUE)//循环
{
L+=1 每次循环边长+1
//用L长的小正方形去填充满边长为d大正方形,那个圆就是这个大正方形的内切圆,假设用了x个小正方形
x=d^2/L^2 //x取整
t=x
for(i=0;i<t;i++) //循环x次
{
检测第i个小正方形的4个顶点是不是在直径为d的内切圆内,是-继续,不是-则x-1
}
比较x和n,当x第一次小于n的时候,中断循环
}
L值即为在n个小正方形填充圆情况下的小正方形边长



不知道楼主是不是这个意思,还是我理解错了。

#5


引用 4 楼 bluekitty 的回复:
刚不给你回了么,你要的不是这个效果么?

可以这样设计算法,不过效率可能比较低,假设圆的直径是d,小正方形的个数是n,按你说的这个n是指定的,可以是任何一个数量,小正方形的边长是L,,但如果是正方形的话不能保证边缘的正方形一定和圆相交,下面是伪代码


Delphi(Pascal) code

L初始为0
while(TRUE)//循环
{
L+=1 每次循环边长+1
//用……

是这个意思,可圆和小正方形都是画线画上去的,怎么判断顶点是不是在园内啊

#6


你画小正方形也得要4个顶点坐标才能画啊,判断一个点是不是在一个圆内,这个。。。。。,这个咱们初中就学过-_-!,这点到圆心的距离小于半径就在园内,大于就在圆外,等于就在圆上,还是给你写个例子

假如一个顶点在v(x,y),圆心在c(x',y')

s=sqrt((x-x')^2+(y-y')^2); //勾股定理,s是到圆心的距离
s<D/2就在园内 //D是直径,所以除以2

#7


正方形大小是否统一?位置是否可以重叠?

#8


引用 7 楼 sz_haitao 的回复:
正方形大小是否统一?位置是否可以重叠?

大小一样,位置不能重叠,必须一个挨一个

#9


那就不算随机了

假设小正方形边长为x
则有x*x种(或更少)排法
全部试一遍就行了

#10


引用 9 楼 sz_haitao 的回复:
那就不算随机了

假设小正方形边长为x
则有x*x种(或更少)排法
全部试一遍就行了

数量是随机的,大小也会随数量缩小,不过全部的正方形都是一样大。
你能将排法说的详细一些吗?

#11


假设小正方形边长为a,就是以园的最左点x坐标m和最上点y坐标n为起始点(m,n),
画出横线(x=m)和竖线(y=n),再移a像素划出横线(x=m+a)和竖线(y=n+a),。。。。它们构成一种正方形的组合
然后以(m+1,n+1)为起始点画出横线(x=m+1)和竖线(y=n+1),再移a像素划出横线(x=m+1+a)和竖线(y=n+1+a),。。。。它们构成第2种正方形的组合
。。。

#12


引用 11 楼 sz_haitao 的回复:
假设小正方形边长为a,就是以园的最左点x坐标m和最上点y坐标n为起始点(m,n),
画出横线(x=m)和竖线(y=n),再移a像素划出横线(x=m+a)和竖线(y=n+a),。。。。它们构成一种正方形的组合
然后以(m+1,n+1)为起始点画出横线(x=m+1)和竖线(y=n+1),再移a像素划出横线(x=m+1+a)和竖线(y=n+1+a),。。。。它们构成第2种正方形的组合
。。。

我请教个问题,delphi能不能先画个网状的图形,然后画个圆,最后只现在圆里圈上的小方格呢

#13


可以啊,循环画横线和竖线,间隔都是a
然后画园
圆心的坐标也是a*a种选择

#14


先说说画这个干嘛。。

#15


引用 13 楼 sz_haitao 的回复:
可以啊,循环画横线和竖线,间隔都是a
然后画园
圆心的坐标也是a*a种选择

能给个简单例子吗?我不太熟悉delphi

#16


引用 14 楼 vividw 的回复:
先说说画这个干嘛。。

涉及保密问题,我不能说啊大哥,这是我的任务,明天交活了,可我现在还没做完,所以到这里请各位高手指教啊。

#17


因为是整形的 算法肯定 不是严格相切的,
这个条件不是严格的 求解是仅仅是美观 。。
 画出来就行了。。

#18


引用 17 楼 vividw 的回复:
因为是整形的 算法肯定 不是严格相切的,
这个条件不是严格的 求解是仅仅是美观 。。
 画出来就行了。。

我现在都没明白咋做呢 我没用过delphi画图 请大哥详细点说啊,最好能给点简单的代码,我也好参考

#19


你用delphi做过什么?
还是用熟悉的 语言写吧。。

#20


引用 19 楼 vividw 的回复:
你用delphi做过什么?
还是用熟悉的 语言写吧。。

要是让用别的语言我早用了。。。只能用delphi做

#21


引用 19 楼 vividw 的回复:
你用delphi做过什么?
还是用熟悉的 语言写吧。。

我第一次用delphi

#22


用穷举法吧。

#23


引用 22 楼 lhylhy 的回复:
用穷举法吧。

高手 ,不要说名词,我不明白,给个简单的例子呗

#24


-_-!,你这样难度太大了,从来没用过delphi怎么写啊,吃晚饭我给你写个吧,就用我那个算法,很简单的

#25


下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:
圆内排列随机个正方形

源代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    PaintBox1: TPaintBox;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure PaintBox1Paint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;



var
  Form1: TForm1;
  Vertexes:array of TPoint;  //存储顶点
  CurL:integer;   //当前的小正方形边长
  DrawState:boolean=false;  //是否可以在OnPaint事件里面绘图

implementation

{$R *.dfm}

procedure AddVertex(vt:TPoint);
begin
  SetLength(Vertexes,Length(Vertexes)+1);
  Vertexes[Length(Vertexes)-1]:=vt;
end;

function RectInPie(vTopLeft,COC:TPoint;CurL,R:integer):boolean;
begin
  Result:=(sqrt(sqr((vTopLeft.X - COC.X))+sqr((vTopLeft.Y - COC.Y)))<=R)  and
          (sqrt(sqr((vTopLeft.X+CurL - COC.X))+sqr((vTopLeft.Y - COC.Y)))<=R) and
          (sqrt(sqr((vTopLeft.X+CurL - COC.X))+sqr((vTopLeft.Y+CurL - COC.Y)))<=R) and
          (sqrt(sqr((vTopLeft.X - COC.X))+sqr((vTopLeft.Y+CurL - COC.Y)))<=R);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
  rClient:TRect;
  i:integer;
  pt:TPoint;
begin
  rClient:=PaintBox1.ClientRect  ;
  with PaintBox1.Canvas do begin
     Brush.Color:=clBlack;
     Brush.Style:=bsSolid;
     Rectangle(0,0,rClient.Right,rClient.Bottom);
     //以上代码把PaintBox1的背景涂成黑色

     //画圆
     Pen.Color:=clYellow;
     Ellipse(0,0,rClient.Right,rClient.Bottom);

     if DrawState then begin //绘制小正方形
       Pen.Color:=clBlue;     //边框蓝色
       Brush.Color:=clLime;   //填充亮绿色
       for i:=0 to Length(Vertexes) -1 do begin
          pt:=Vertexes[i];
          Rectangle(pt.X,pt.Y,pt.X+CurL,pt.Y+CurL);
       end;
     end;
  end;//end with
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  L,n,r,x:integer;
  nCount,nCountPerLine,nLine,i:integer;
  V:TPoint;    //左上角顶点
begin
  if not TryStrToInt(Edit1.Text,n) then exit; //指定用多少个小正方形
  DrawState:=false; //暂时不允许绘图
  r:=PaintBox1.ClientRect.Right div 2;       //圆半径
  L:=3;      //小正方形边长

  while true do begin

    Inc(L); //从4开始,太小没意思
    SetLength(Vertexes,0); //清空顶点
    x:=sqr(2*r) div sqr(L);   //求要用多少个小正方形填充大正方形
    nCount:=x;
    nCountPerLine:=Round(2*r/L); //一行有多少个小正方形
    nLine:=0;  //从第一行开始

    for i:=1 to x do begin

      if i mod nCountPerLine=0 then Inc(nLine);//换行

      //求小正方形的左上角坐标
      V.X:=L*(i mod nCountPerLine);
      V.Y:=nLine*L;

      if RectInPie(V,Point(r,r),L,r) then
        AddVertex(V)
      else
        Dec(nCount);

    end;  //end for

    if nCount<n then begin
      CurL:=L;    //求得小正方形边长,并终止循环
      Label2.Caption:=Format('实际用了 %d 个正方形(边长%d)',[nCount,CurL]);
      break;
    end;

  end; //end while

  DrawState:=true;
  PaintBox1.Invalidate;
  
end;

end.

#26


引用 25 楼 bluekitty 的回复:
下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:


源代码:
Delphi(Pascal) code

……

多谢大哥

#27


引用 25 楼 bluekitty 的回复:
下面是代码,由于屏幕坐标都是整数坐标,所以无法精确的画出指定个数的小正方形,而且图形并不对称,如果要对称的话,可以只计算1/4的顶点数,就是垂直和水平两条直径把园分成4分,只求其中一份的所有定点,然后推算出其3个部分的顶点坐标,这样图形就对称了,而且效率更高,不过可能实际的小正方形数量会更不准确,你自己试验一下。
结果如下图:


源代码:
Delphi(Pascal) code

……

大哥,数量少了是什么原因,我应该改什么地方能让数量接近些

#28


你是用数量去推导边长,而且要填满一个圆形,这样不太正确,因为实际上边长是可以确定的,而数量不能确定,原因就是由于屏幕坐标都是整数,无法精确的绘制图形,边长的变化和数量的变化是离散的,可能边长增加或减少一点,数量不变,也可能边长每次增加10而每次减少的数量都不一样,即假如指定100个等大的正方形填满一个圆,在GDI模式实际上可能根本做不到,除非不填满。下面是优化过的代码,你可以再试一下,这样图形是上下左右对称的,好看而且效率高,不过数量更不准确

procedure TForm1.Button1Click(Sender: TObject);
var
  L,n,r:integer;
  nCount,nCountPerLine,nLine,i,t:integer;
  V:TPoint;
begin
  if not TryStrToInt(Edit1.Text,n) then exit; //指定用多少个小正方形
  ASSERT(n mod 4=0,'数目应该为4的倍数');
  DrawState:=false; //暂时不允许绘图
  r:=PaintBox1.ClientRect.Right div 2;       //圆半径
  L:=3;      //小正方形边长

  while true do begin  //求左上1/4圆周那个扇形可以容纳多少个边长为L的小正方形

    Inc(L);
    SetLength(Vertexes,0); //清空顶点

    nLine:=r div L;//一共nLine行

    for i:=1 to nLine do begin
      //计算第i行可以容纳多少个边长为L的小正方形
      nCountPerLine:=Trunc(sqrt(sqr(r)-sqr(i*L))/L); //截断,不要四舍五入
      for t:=1 to nCountPerLine do begin
        V.X:=r-t*L;
        V.Y:=r-i*L;
        AddVertex(V);
      end;//end 2 for

    end; //end 1 for

    nCount:=Length(Vertexes);

    ASSERT(nCount>0);

    if nCount<=n div 4 then begin
      CurL:=L;
      break;
    end

  end;//end 1 while



  for i:=0 to Length(Vertexes)-1 do  //右上侧
    AddVertex(Point(2*r-Vertexes[i].X-CurL,Vertexes[i].Y)); //横坐标和垂直半径对称

  for i:=0 to Length(Vertexes)-1 do  //上半圆整个沿水平直径再翻一次
     AddVertex(Point(Vertexes[i].X,2*r-Vertexes[i].Y-CurL));

  Label2.Caption:=Format('实际用了 %d 个小正方形(边长%d)',[Length(Vertexes),CurL]);
  DrawState:=true;
  PaintBox1.Invalidate;

end;

#29


有两种对称形状,一种是圆点是一个格子中间的,另一种是圆点是四个格子交界的。

#30


引用 28 楼 bluekitty 的回复:
你是用数量去推导边长,而且要填满一个圆形,这样不太正确,因为实际上边长是可以确定的,而数量不能确定,原因就是由于屏幕坐标都是整数,无法精确的绘制图形,边长的变化和数量的变化是离散的,可能边长增加或减少一点,数量不变,也可能边长每次增加10而每次减少的数量都不一样,即假如指定100个等大的正方形填满一个圆,在GDI模式实际上可能根本做不到,除非不填满。下面是优化过的代码,你可以再试一下,这样图形是……

多谢了