como construir programaticamente uma grade de quadrados interligados, mas de tamanho aleatório

8

Quero criar um layout bidimensional de formas retangulares, uma grade composta de cubos de tamanho aleatório. O cubo deve se encaixar e ter preenchimento ou margem iguais (espaço entre eles). Como um layout de quadrinhos ou mais como a imagem anexada.

Como eu poderia fazer isso proceduralmente?

Na prática, eu provavelmente usaria Python e algum software gráfico para renderizar uma imagem, mas não sei o tipo de algoritmo (ou outros enfeites) que precisaria usar para gerar a grade aleatória.

cubos aleatórios

Mrwolfy
fonte
Realmente, isso não é um problema de programação. Você precisa inventar e decidir o método - também conhecido como 'algoritmo' - pelo qual deseja os quadrados; implementá-lo é um problema de programação. Para inventar o método? Comece com um pedaço de papel em branco - de preferência muitos pedaços de papel - faça o desenho e descreva o que está fazendo.
AakashM

Respostas:

8

Comece com uma grade de células 1x1. Escolha um ponto aleatório, mescle as células uma quantidade aleatória ou até que colide com um retângulo maior.

Isso fornecerá algo semelhante à imagem que você fornece.

Há um problema maior se você não quiser um número de células menores atuando como preenchimento entre suas células maiores. Uma história em quadrinhos, por exemplo, quer minimizar a quantidade de espaço morto e ter no máximo ~ 9 células. Você pode escolher alguns pontos, desenhar algumas linhas e chamá-las de células.

//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;
}

Há muito mais maneiras de esfolar um gato.

Philip
fonte
7
  • Comece com um quadrado que descreva a imagem inteira.
  • Adicione o quadrado a uma matriz vazia.

  • Para cada quadrado na matriz:

    • Crie um booleano com um valor aleatório verdadeiro / falso.
    • Se o valor for verdadeiro:
      • Divida o quadrado em quatro sub-quadrados do mesmo tamanho.
      • Adicione-os ao final da matriz.
      • Remova o quadrado atual da matriz.

No final do processo, você terá uma matriz de quadrados de tamanho aleatório. Observe que você provavelmente desejará definir um tamanho mínimo (quando não for mais dividida) e um tamanho máximo (se o quadrado for maior, sempre separe independentemente do valor booleano).

Formiga
fonte
1
Parece uma boa solução.
Mrwolfy
(Usei um algoritmo semelhante para um gerador de labirinto recursivo na faculdade) Isso terá o efeito colateral de sempre ter linhas que cortam a imagem inteira.
@ MichaelT: Definitivamente, um problema, mas a simplicidade e a velocidade do algoritmo compensa isso. Realmente depende do caso de uso se essa é uma abordagem apropriada para o problema. A imagem de exemplo parece ter sido gerada com esse algoritmo, pois sofre com o problema destacado.
Ant
1

Determine o tamanho e a geometria da imagem. Se você deseja que isso seja lado a lado, a geometria subjacente é a de um toro.

Mantenha uma lista do canto superior esquerdo de todas as formas possíveis. Inicialmente, este é todo ponto possível.

Escolha um retângulo de tamanho aleatório (dentro das restrições que você decidiu - na imagem de exemplo, eles são quadrados e têm um tamanho máximo de 4). Coloque esse retângulo em um local aleatório no canto superior esquerdo.

Se o retângulo for muito grande (sobrepõe um ponto alocado existente), apare a dimensão do retângulo para que ele se ajuste.

Remova todos os locais cobertos por esse retângulo da lista de possíveis cantos superiores esquerdo.

Repita até que a lista do canto superior esquerdo esteja vazia.

Renderize a matriz resultante da sua maneira preferida. É aqui que você introduziria uma margem.


Se você não estiver familiarizado com uma biblioteca de gráficos específica, considere usar o formato ppm . A principal vantagem é que você pode gravar um arquivo de texto e usar um dos conversores (ppmto___) para converter a imagem no formato escolhido (ppmtogif. Ppmtojpeg, ...)


fonte
Obrigado. Na verdade, estou mais interessado em convertê-lo em um conjunto de pontos centrais (em uma grade x, y) e descrições de escala (como em 1 ou 0,5 ou 2, por exemplo). Vou alimentar esses números em um aplicativo 3D.
Mrwolfy