web-dev-qa-db-ja.com

プログラムで連動するがランダムなサイズの正方形のグリッドを構築する方法

長方形の2次元レイアウト、つまりランダムなサイズの立方体で構成されるグリッドを作成したいと思います。立方体は互いにフィットし、パディングまたはマージン(間隔)が等しい必要があります。コミックのレイアウトのようなもの、または添付された画像のようなもの。

どうすればこれを手続き的に行うことができますか?

実際には、おそらくPythonといくつかのグラフィックソフトウェアを使用して画像をレンダリングしますが、ランダム化されたグリッドを生成するために使用する必要があるアルゴリズムの種類(またはその他)がわかりません) 。

random cubes

8
Mrwolfy

1x1セルのグリッドから始めます。ランダムなスポットを選択し、セルをランダムに結合するか、それがより大きな長方形と衝突するまで。

これにより、提供するイメージに似たものが得られます。

大きなセル間のパディングとして機能する小さなセルが多数必要ない場合は、大きな問題があります。たとえば漫画本では、デッドスペースの量を最小限に抑え、セル数を最大で9個に抑えたいと考えています。いくつかの点を選択して線を引き、それらをセルと呼ぶことができます。

//Philip Haubrich, 2012, public domain
//Build instructions: gcc comicPanels.c

#include <stdio.h>  //entirely for printf()
#include <stdlib.h> //Entirely for Rand()
#include <time.h> //Entirely to help srand()

#define PAINTINGSIZE_X 79
#define PAINTINGSIZE_Y 20

#define MYNUMBEROFPOINTS 4

#define MINDISTANCEBETWEENBOXES 2
//Because I suck at naming things. You should really fix this before it gets into your codebase.

#define NORTH 0
#define EAST 1
#define SOUTH 2
#define WEST 3

#define WHITE 0
#define BLACK 1

//Or, you know, a struct with .color, .r .g .b .alpha .editablebydeadpool
char g_paintingArea[PAINTINGSIZE_X][PAINTINGSIZE_Y];

void drawLineUntilBlocked(int x, int y, int direction)
{
  do
  {
    g_paintingArea[x][y] = BLACK;
    switch(direction)
    {
    case NORTH:
      y++;
      break;
    case SOUTH:
      y--;
      break;
    case EAST:
      x++;
      break;
    case WEST:
      x--;
      break;
    default:
      printf("I really need to get away from switch statements...\n");
    }
  } while(g_paintingArea[x][y] == WHITE && x > 0 && y > 0 && x < PAINTINGSIZE_X && y < PAINTINGSIZE_Y);
  //dowhile, when you are too lazy to re-arrange the code
}

//Feel free to sub in something like SDL or openGL here
void Paint()
{
  int x,y;
  for(y=0; y<PAINTINGSIZE_Y; y++)
  {
    for(x=0; x<PAINTINGSIZE_X; x++)
    {
      printf("%c",g_paintingArea[x][y]);
    }
    printf("\n");
  }
}

int empty(int origx, int origy)
{
  int x,y;
  for(x=origx-MINDISTANCEBETWEENBOXES; x<origx+MINDISTANCEBETWEENBOXES; x++)
  {
    for(y=origy-MINDISTANCEBETWEENBOXES; y<origy+MINDISTANCEBETWEENBOXES; y++)
    { 
      if( x < 0 || y < 0 || x >= PAINTINGSIZE_X || y >= PAINTINGSIZE_Y)
        continue;
      if( g_paintingArea[x][y] == BLACK)
        return 0; //Not empty, there is something nearby
    }
  }
  return 1; //Empty, like my heart
}

void init()
{
  int x,y;
  //initalize to zero
  for(x=0; x<PAINTINGSIZE_X; x++)
  {
    for(y=0; y<PAINTINGSIZE_Y; y++)
    {
      g_paintingArea[x][y] = WHITE;
    }
  }
  //Border, or as I like to call it B-town
  for(x=0; x<PAINTINGSIZE_X; x++)
  {
    g_paintingArea[x][0] = BLACK;
    g_paintingArea[x][PAINTINGSIZE_Y-1] = BLACK;
  }
  for(y=0; y<PAINTINGSIZE_Y; y++)
  {
    g_paintingArea[0][y] = BLACK;
    g_paintingArea[PAINTINGSIZE_X-1][y] = BLACK;
  }

  //oh yeah, this is important
  x = abs(time(NULL));
  srand(x);
}

int main(int argc, char** argv)
{
  int x,y,i; 

  init();

  //That part you actually asked about
  for( i=0; i<MYNUMBEROFPOINTS; i++)
  {
    x = Rand() % PAINTINGSIZE_X;
    y = Rand() % PAINTINGSIZE_Y;

    if(!empty(x,y))
      continue;

    switch(Rand()%3)
    {
    case 0: //4 way
      drawLineUntilBlocked(x,y,NORTH);
      drawLineUntilBlocked(x,y,SOUTH);
      drawLineUntilBlocked(x,y,EAST);
      drawLineUntilBlocked(x,y,WEST);
      break;
    case 1: //North/sourth
      drawLineUntilBlocked(x,y,NORTH);
      drawLineUntilBlocked(x,y,SOUTH);
      break;
    case 2: //East/West
      drawLineUntilBlocked(x,y,EAST);
      drawLineUntilBlocked(x,y,WEST);
      break;
    default:
      printf("Oh god wtf, and other useful error messages\n");
    }
  }
  //If I have to explain to you that this next bit will depend on your platform, then programming may not be for you
  Paint();  
  return 0;
}

猫の皮をむく方法はもっとたくさんあります。

8
Philip
  • 画像全体を説明する正方形から始めます。
  • 正方形を空の配列に追加します。

  • 配列内の各正方形について:

    • ランダムなtrue/false値でブール値を作成します。
    • 値がtrueの場合:
      • 正方形を4つの等しいサイズのサブ正方形に分割します。
      • それらを配列の最後に追加します。
      • 現在の正方形を配列から削除します。

プロセスの最後に、ランダムなサイズの正方形の配列ができます。おそらく、最小サイズ(この時点では分割は行われません)と最大サイズ(正方形が大きい場合は、ブール値に関係なく常に分割)を定義する必要があることに注意してください。

7
Ant

画像のサイズとジオメトリを決定します。これをタイリングしたい場合は、基になるジオメトリはトーラスのジオメトリです。

可能なすべての形状の左上隅のリストを維持します。最初は、これがすべての可能なスポットです。

ランダムなサイズの長方形を選択します(決定した制約内で-サンプル画像では正方形で、最大サイズは4です)。この長方形をランダムな左上隅のスポットに配置します。

長方形が大きすぎる(既存の割り当てられたスポットに重なる)場合は、長方形が収まるように寸法をトリミングします。

可能な左上隅のリストから、この長方形で覆われているすべての場所を削除します。

左上隅のリストが空になるまで繰り返します。

結果の配列を好みの方法でレンダリングします。これは、マージンを導入する場所です。


特定のグラフィックスライブラリに慣れていない場合は、 ppm形式 の使用を検討してください。主な利点は、テキストファイルを書き出してから、コンバーター(ppmto___)の1つを使用して、選択した形式(ppmtogif。ppmtojpegなど)に画像を変換できることです。

1
user40980