最小化绘制线的算法?

时间:2015-06-27 12:07:23

标签: algorithm autohotkey

我目前正致力于优化我现在已有几年历史的项目。目的是在击中某个键组合后绘制图像。我几年前制作的原始版本手动移动到每个方形区域,但我最近对其进行了优化,以绘制连续方块的矩形,这使得它更快。

我想采取的下一步是优化程序绘制给定布局的方式,但我不知道从哪里开始寻找。我希望有人可以指出我正确的方向,因为我甚至无法想到一个搜索术语。

目前该程序有一个名为Draw的函数,它接受如下输入:

Invader =
(
00100000100
00010001000
00111111100
01101110110
11111111111
10111111101
10100000101
00011011000
)

Draw(Invader, 10) ; Where 10 is the size in pixels of each square

上面的布局适用于此图片:

enter image description here

Draw将采用该布局,并按以下方式从上到下,从左到右绘制:

enter image description here

总共需要18个单独的部分来完成图片。我正在寻找的是一些可以最小化这个数字的算法。例如,以下是仅有16个部分的少数方法之一:

enter image description here

同样地,当前的方式和我刚刚为这张图片制作的东西之间的差异是19(65与46相比)。

enter image description here

我应该从哪里开始?

另外作为参考,这是当前的Draw函数:

Draw(Layout, BlockSize)
{
  Len := StrLen(Layout)                         ; Total amount of characters
  RowSize := StrLen(StrSplit(Layout, "`n")[1])  ; Size of a single row
  Index := 0

  While (Index < Len)
  {
    Length := 1
    Char := GetChar(Layout, Index)  ; Get next character in string

    if (Char == "1")
    {
      ; Get the number of consecutive 1s
      While (GetChar(Layout, Index + Length) == "1")
      {
        Length := Length + 1
      }

      ; Draw the rectangle
      FillRectangle(Length, BlockSize)
    }
    else if (Char == "0")
    {
      ; Get the number of consecutive 0s
      While (GetChar(Layout, Index + Length) == "0")
      {
        Length := Length + 1
      }

      ; Skip the entire length
      MouseMove, BlockSize * Length, 0, 0, R
    }
    else
    {
      ; End of line, reset position
      MouseMove, -(RowSize * BlockSize), BlockSize, 0, R
    }

    Index := Index + Length
  }
}

FillRectangle(Width, BlockSize)
{
  MouseGetPos, mX, mY
  mY2 := mY                     ; Same Y for straight line
  mX2 := mX + Width * BlockSize ; Add Width of rectangle times the block size to get final X position

  Loop %BlockSize%
  {
    ; Draw line
    MouseClickDrag, L, mX, mY, mX2, mY2

    ; Move to next line
    mY -= 1
    mY2 -= 1
  }

  ; Move mouse to next position
  MouseMove, 0, BlockSize - 1, 0, R
}

GetChar(String, Index)
{
  return SubStr(String, Index, 1)
}

2 个答案:

答案 0 :(得分:1)

你应该先进行某种分析。之后我建议双向传递“图像”并保留较长的行(将较长行的每个单元格标记为已通过或“黑色”,这样您就不会重复检查)。

void analyze(){
     var horSize = 0, verSize = 0;

     // run horizontally & vertically for each white cell
     while(!reached_boundary){
         ++horSize ;   
     }
     while(!reached_boundary){
         ++verSize ;   
     }
     someContainer.Add( (horSize > verSize) ? horSize:verSize);
}

答案 1 :(得分:1)

这是从EpiGen的答案中扩展而来的,但我觉得需要自己的帖子来解释这些差异。

这是我现有的状态,但在所有情况下仍然不是100%最优(如下所示)。如果有任何改进,请随意添加。

因此,算法的基本流程如下:

  1. 从当前点获取水平长度
  2. 从当前点获取垂直长度
  3. 选择更长的长度并使用该方向
  4. 然而,它并没有给出它看到的长度。相反,它选择的最大长度不会与具有更长长度的线相交。以下是步骤:

    1. 检查下一个像素是否为1(水平向右,垂直向下)
    2. 如果是,则从该索引开始检查相反方向的长度。
    3. 如果该长度超过当前长度,则保存当前长度和相反的长度值。
    4. 一旦看到一个不是1的字符,如果被检查方向的最大长度低于相反方向的最大长度,则在该点之前返回我们方向的长度。
    5. 以下是此逻辑的实例示例。灰线表示已绘制的线条,绿线表示正在检查的线条,红线表示边界。

      enter image description here

      由于红线的水平长度大于此时的当前垂直长度,因此值以当前形式保存(垂直1,水平7)。在垂直线检查完成并找到长度为2之后,它会看到它越过了一条长度为7的线。由于将该线分割为较小的线的效率较低,因此它将其长度改为1。这就是它越过那条线之前所拥有的。这使得最终输出看起来像这样,共有16个段,据我所知这是最佳的。

      enter image description here

      但是,它在某些条件下会失败;特别是这张图片的左下角。

      enter image description here

      绿线的长度为10,它停在的行的长度为9.由于该行不大于或等于其大小,它会将离开单个块的行拆分为侧。如果这个问题得到解决,那么就我所知,这个图像将是最佳的。 (最低的我得到的是44,当前的逻辑得到45)。

      无论如何,这似乎和我需要的一样好。如果在第二天左右有更好的解决方案有任何其他答案,我会看看它们。

      作为一个额外的,这里有一个大型的GIF运行:

      enter image description here