C++ create random shaped "blob" objects
Asked Answered
A

3

8

I need to define an object (or a region) that is kind of "blob" shaped on a discrete gridmap. It should look something like this: enter image description here

where the red region denotes the center point (these are just ideas, any blobby shape will work as long as it can be varied randomly). My idea so far was to iteratively increment the angle from starting point (=0 degrees) to 360 degrees and use trigonometry to calculate the outer points of a circle (which will result in the unit circle if the radius = 1 = const). I then used Bresenham's line algorithm (remember: we are moving on a discrete grid) to calculate the line that connects the center of the circle and the outer point I just came up with. My idea was that if I could vary the radius somewhat, I could create these blobby shapes. What I've come up with so far gives me nice shapes, they're just not really "blobby" though. Here's my code (note that x0 and y0 mark the center point of my gridmap, plotBresenham just puts all 1s in the regions so the gridmap can be visualized):

double radius = 10; 
for(int alpha=0; alpha<360; alpha++) {
   double x = cos(alpha*M_PI/180.0)*radius;
   double y = sin(alpha*M_PI/180.0)*radius;

   if(alpha<45) radius+=0.5;
   else if(alpha<90) radius-=0.5; 
   else if(alpha<135) radius+=0.5; 
   else if(alpha<180) radius-=0.5; 
   else if(alpha<225) radius+=0.5; 
   else if(alpha<270) radius-=0.5; 
   else if(alpha<315) radius+=0.5; 
   else radius-=0.5; 

   plotBresenhamLine(x0,y0,x,y)

}

The result looks like this:

enter image description here

Sorry for the crude drawing. Programming language is C++, but I think the approach doesn't really depend on the language used. Any tips / help / guidance on how I can create shapes that resemble the ones I need more? Or even a Framework that does stuff like this for you? For me, it's just important to have the coordinates of the points within, to put them into my gridmap.

Arris answered 22/2, 2019 at 13:17 Comment(2)
Why not throw some rand() in there?Chemism
I did exactly this - but the values of rand() varied too much, and the resulting shape looked somewhat like a sea urchin! Maybe a random way to draw samples from a distribution while ensuring that they are not too far apart would be the way to go!Arris
O
13

Varying the radius with the angle is the way to go. But instead of the random walk you can use a sum of several periodic functions with predetermined amplitudes and phases. This guarantees that

  1. the radius will come back to its original value after a turn of 360°,
  2. you can easily control the range of radii you encounter. (You need to avoid going less than zero).

Pick a sine or cosine function where you multiply the angle by an integer and add a random phase. Scale each by a random (pre-determined) amplitude. Add a constant larger than the sum of all the amplitudes. Profit.

I'm not going to write this in C++ because, as you said, it won't add anything important to the algorithm. It may go like this:

  1. Take N, the number of waves you'd like.
  2. Define float arrays amps[N] and phases[N].
  3. Pick a random number between 0 and 1/(2N) for each amps[i] and between 0 and 2π for each phases[i].
  4. For each angle alpha (in radians), calculate
radius = 1 + sum[i=0 to N-1] amps[i] * cos((i+1)*alpha + phases[i])
x = cos(alpha)*radius;
y = sin(alpha)*radius;
  1. Proceed as before.

Results (from Wolfram Mathematica):

To make it somewhat more interesting, limit the k-th amplitude by some negative power of k (or of k+1, since we're indexing from zero). Here's when instead of 2N the random number is divided by pow(i+1,1.5) in step 3, for N = 30:

Oui answered 22/2, 2019 at 14:17 Comment(4)
Perfect, that's exactly what I was looking for! I don't really understand the theory behind these plots though - could you point me to some literature or keywords that adress this topic? Cheers!Arris
@Arris The starting point is Fourier series. They can represent any periodic function (and periodic is what you want here, see first paragraph) and of course random Fourier = random result. The part with the power law has some rooting in deeper properties of the theory but you can simply say it limits the influence of rapid oscillations (try N=30 without the power law to see why that is a problem).Oui
By the way small correction in step 4, y = sin(alpha)*radius;Trihedron
This looks really useful. I will try playing around with it to see if it can produce more like paint splatter. Like blobs that almost pull away but don't separate. Maybe that's just forcing some amps[] to be high? But I feel like the returning curves would not get skinny. What I'm describing is more like two or more of these blobs connected by bezier curvesOrchard
O
1

Here is a C version of the solution from "The Vee" that gets close to what I'm looking for, but I'm trying to find a way to invert the curve for some of the parts, which would make these "nodes" that stretch out look like they're starting to pinch off.

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main() {
   time_t t;
   uint16_t alpha, i;
   uint16_t N = 12;
   float amps[N];
   float phases[N];
   float radius, x, y;

   srand((unsigned) time(&t));
   srand((unsigned) rand());

   for (i=0; i<N; i++) {
     float s = (rand() / (float) RAND_MAX);
     amps[i] = 1.0/(2.0*N) * s * 4;
     phases[i] = s * 2.0 * M_PI;
   }

   for (alpha=0; alpha<360; alpha++) {
     float radian = alpha * ( M_PI / 180.0 );
     radius = 1;
     for (i=0; i<N-1; i++) {
       radius = radius + (amps[i] * cos((i+1)*radian + phases[i]));
     }
     x = cos(radian)*radius;
     y = sin(radian)*radius;
     printf("%2.16f, %2.16f\n", x, y);
   }
}

enter image description here

Orchard answered 17/7, 2023 at 14:24 Comment(0)
T
1

This is a followup for The Vee's great answer.

For anyone stumbling upon this question and is looking for an implementation. Python code, it can be written like so.

from random import random
from math import cos, sin, pi, radians

N = 10
amps = [random() * (1 / (2*N)) for _ in range(N)]
phases = [random() * 2 * pi for _ in range(N)]

x = []
y = []
for deg in range(360):
    alpha = radians(deg)
    radius = 1 + sum([amps[i] * cos((i+1)*alpha + phases[i]) for i in range(N)])
    x.append(cos(alpha) * radius)
    y.append(sin(alpha) * radius)

Trapezius answered 31/7, 2023 at 11:12 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewWinkler

© 2022 - 2025 — McMap. All rights reserved.