cómo construir programáticamente una cuadrícula de cuadrados entrelazados pero de tamaño aleatorio

8

Quiero crear un diseño bidimensional de formas rectangulares, una cuadrícula compuesta de cubos de tamaño aleatorio. Los cubos deben encajar y tener un relleno o margen igual (espacio entre ellos). Algo así como un diseño de cómic, o más como la imagen adjunta.

¿Cómo podría hacer esto procesalmente?

Prácticamente, probablemente estaría usando Python y algún software gráfico para renderizar una imagen, pero no sé qué tipo de algoritmo (o qué) necesitaría usar para generar la cuadrícula aleatoria.

cubos al azar

Mrwolfy
fuente
Realmente, esto no es un problema de programación. Debe idear y luego decidir sobre el método, también conocido como 'algoritmo', por el cual desea los cuadrados; implementarlo es entonces un problema de programación. ¿Para idear el método? Comience con un trozo de papel en blanco, preferiblemente muchos trozos de papel, dibuje y luego describa lo que está haciendo.
AakashM

Respuestas:

8

Comience con una cuadrícula de 1x1 celdas. Elija un lugar aleatorio, combine celdas una cantidad aleatoria o hasta que choque con un rectángulo más grande.

Esto te dará algo similar a la imagen que proporciones.

Hay un problema mayor si no desea que varias celdas más pequeñas actúen como relleno entre sus celdas más grandes. Un cómic, por ejemplo, quiere minimizar la cantidad de espacio muerto y tener como máximo ~ 9 celdas. Podrías elegir algunos puntos y dibujar algunas líneas y llamarlas celdas.

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

Hay muchas más formas de pelar un gato.

Philip
fuente
7
  • Comience con un cuadrado que describa toda la imagen.
  • Agregue el cuadrado a una matriz vacía.

  • Para cada cuadrado en la matriz:

    • Cree un booleano con un valor aleatorio verdadero / falso.
    • Si el valor es verdadero:
      • Divide el cuadrado en 4 subcuadrados de igual tamaño.
      • Agréguelos al final de la matriz.
      • Eliminar el cuadrado actual de la matriz.

Al final del proceso, tendrá una matriz de cuadrados de tamaño aleatorio. Tenga en cuenta que probablemente querrá definir un tamaño mínimo (en cuyo punto no se dividirá más) y un tamaño máximo (si el cuadrado es más grande, siempre divídalo independientemente del valor booleano).

Hormiga
fuente
1
Esto parece una buena solución.
Mrwolfy
(Utilicé un algoritmo similar para un generador de laberinto recursivo en la universidad) Esto tendrá el efecto secundario de tener siempre líneas que corten toda la imagen.
@MichaelT: Definitivamente es un problema, pero la simplicidad y la velocidad del algoritmo lo compensan. Realmente depende del caso de uso si este es un enfoque apropiado para el problema. La imagen de ejemplo parece haberse generado con este algoritmo ya que sufre el problema que resaltó.
Ant
1

Determine el tamaño y la geometría de la imagen. Si quiere que esto sea en mosaico, la geometría subyacente es la de un toro.

Mantenga una lista de la esquina superior izquierda de todas las formas posibles. Inicialmente, este es cada lugar posible.

Elija un rectángulo de tamaño aleatorio (dentro de las restricciones que haya decidido; en la imagen de ejemplo son cuadrados y tienen un tamaño máximo de 4). Coloque este rectángulo en un lugar aleatorio en la esquina superior izquierda.

Si el rectángulo es demasiado grande (se superpone a un punto asignado existente), recorte la dimensión del rectángulo para que encaje.

Elimine todas las ubicaciones cubiertas por este rectángulo de la lista de posibles esquinas superiores izquierdas.

Repita hasta que la lista de la esquina superior izquierda esté vacía.

Renderice la matriz resultante de la manera que prefiera. Aquí es donde introducirías un margen.


Si no está familiarizado con una biblioteca de gráficos en particular, considere usar el formato ppm . La ventaja clave es que puede escribir un archivo de texto y luego usar uno de los convertidores (ppmto___) para convertir la imagen al formato elegido (ppmtogif. Ppmtojpeg, ...)


fuente
Gracias. De hecho, estoy más interesado en convertirlo en un conjunto de puntos centrales (en una cuadrícula x, y) y descripciones de escala (como en 1 o .5, o 2 por ejemplo). Alimentaré estos números en una aplicación 3D.
Mrwolfy