Create number of random rectangles inside bigger outer rectangle without intersect each other

If I have an outer rectangle and need to create some of the small inner rectangles by random without intersecting each other

what is the best workflow to do that?

Inner rectangles inside outer bigger rectangle

Two options are below.

  1. Make it pseudorandom. Divide your surface into a 2 way grid and inset boxes in each cell by random ranges. This means no rectangle could overlap as they are bound to their cell.

  2. Use Python and a while loop to generate shapes iteratively and test for intersections each time. If one occurs omit it from the results and keep the loop going until a desired number of rectangles are made. Quite complex and dangerous assuming you ask for too many rectangles to fit. Make sure to use an overload counter concurrently ro set a maximum number of iterations to run.

If 2 doesn’t make any sense I’d focus on 1 as it can be done without Python.

2 Likes

One need not use Python, but imperative Design Script would be a pre-req for the alternative.

You could also use some bin packing nodes followed up with a light relaxation algorithm to space them out. Without seeing the base inputs and constraints it is tough to give better guidance though.

4 Likes

Thanks for your reply, if this option need design script

I invite design script leader @Vikram_Subbaiah to help me for that :blush:

1 Like

Will certainly contribute if I can, but as @jacob.small suggested above, you will need to outline the goal better. Providing some context and samples of your attempts will help the rest of us understand your objective.

2 Likes

I may fold a similar exercise into a coming webinar. If so I will drop a definition which could get you started later.

1 Like

Something to try:


def populateRectangles (srf,count)
 {
    rectangles = [Imperative]
    {
        attempts = 0;
        results = [];
        while (DSCore.List.Count(results) < count && attempts < 10)
        {
            attempts = attempts +1;

            csSet =
               Surface.CoordinateSystemAtParameter(
                    srf,
                    Math.RandomList(count),
                    Math.RandomList(count)
                );

            rects =
                Rectangle.ByWidthLength(
                    Math.RandomList(count/2),
                    Math.RandomList(count/2)
                );

            rects =
                Surface.ByPatch(
                    Geometry.Transform(
                        rects,
                        csSet
                    )
                );
            rects = DSCore.List.Join([results, rects]);

            set = [];
            for (i in0..DSCore.List.Count(rects))
            {
                set =
List.AllFalse(Geometry.DoesIntersect(rects[i],set))?
                        DSCore.List.Join([set,rects[i]]):
                        set;

            };

            results = set;
        };
return = results;
        rectangles = Geometry.Intersect(rects,srf);
    };
    rectSet = DSCore.List.Flatten(Geometry.Intersect(rectangles, srf),-1);
return = rectSet[0..count-1];
 };

Having some formatting errors (posting from my phone due to some internet issues), but the actual definition can be seen in this screenshot, where I placed up to 1000 rectangles in a 5x5 square.

14 Likes

Wizardry. I stand corrected - glad to see someone proposed an option 3!

1 Like

Awesome, Jacob :slight_smile:

Scaled rectangles in a grid to create this …

rectSub.dyn (11.6 KB)

// Dimensions
wdt01 = 50;
lgt01 = 50;
cnt01 = 100;

// Divisions
are01 = wdt01 * lgt01;
sqr01 = Math.Ceiling(Math.Sqrt(cnt01));
prp01 = wdt01/lgt01;
wdv01 = Math.Ceiling(sqr01*prp01);
ldv01 = Math.Ceiling(cnt01/wdv01);

// Scaled
rct00 = Rectangle.ByWidthLength(wdt01,lgt01);
rct01 = Rectangle.ByWidthLength(wdt01/wdv01,lgt01/ldv01);
rct02 = rct01.Translate((-wdt01+(wdt01/wdv01))/2,(-lgt01+(lgt01/ldv01))/2,0);
rct03 = List.Flatten(rct02.Translate((0..#wdv01..(wdt01/wdv01))<1>,(0..#ldv01..(lgt01/ldv01))<2>,0));
rnd01 = Math.RemapRange(Math.RandomList(List.Count(rct03)),0.3,0.9);
rnd02 = Math.RemapRange(Math.RandomList(List.Count(rct03)),0.3,0.9);
pln01 = Plane.ByOriginNormal(rct03.Center(),Vector.ZAxis());
rct04 = List.TakeItems(List.Shuffle(rct03.Scale(pln01,rnd01,rnd02,1)),cnt01);

// Third Dimension
rnd03 = Math.RemapRange(Math.RandomList(List.Count(rct03)),1,5);
rnd04 = Math.Ceiling(List.Chop(Math.RemapRange(Math.RandomList(List.Count(rct03)*3),0,255),List.Count(rct03)));
clr01 = Color.ByARGB(200,rnd04[0],rnd04[1],rnd04[02]);
cbd01 = rct04.ExtrudeAsSolid(rnd03);
cbd02 = GeometryColor.ByGeometryColor(cbd01,clr01);
[rct00,cbd02];
7 Likes

Thanks @jacob.small
Thanks @Vikram_Subbaiah
Wow it’s very useful

1 Like

Another Pythonic alternative :grinning:

randomRectangle

import sys
import clr
import System
import random
import math
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('DSCoreNodes')
from DSCore import *
clr.AddReference('GeometryColor')
from Modifiers import GeometryColor

def generator(container, nbr = 1, ratioMax = 5):
    outrect = []
    bbxcont = container.BoundingBox
    minPoint = bbxcont.MinPoint
    maxPoint = bbxcont.MaxPoint
    w = maxPoint.X - minPoint.X
    h = maxPoint.Y - minPoint.Y
    minDim = minPoint.DistanceTo(maxPoint) / nbr
    minSurf = (w * h) / nbr
    i = 1
    while len(outrect) < nbr and i < 200000:
        pta = Point.ByCoordinates(random.uniform(minPoint.X, maxPoint.X ), random.uniform(minPoint.Y, maxPoint.Y ), 0)
        ptd = Point.ByCoordinates(random.uniform(pta.X, w/nbr), random.uniform(pta.Y ,h/nbr), 0.1)
        bbxCandiate = BoundingBox.ByCorners(pta, ptd)
        w1 = bbxCandiate.MaxPoint.X - bbxCandiate.MinPoint.X
        h1 = bbxCandiate.MaxPoint.Y - bbxCandiate.MinPoint.Y
        if any((x[0].Intersects(bbxCandiate)) for x in outrect) or w1 * h1 > minSurf or w1 / h1 > ratioMax or h1 / w1 > ratioMax:
            pass
        else:
            geom = bbxCandiate.ToCuboid()
            color = Color.ByARGB(255,random.randint(0,255),random.randint(0,255),random.randint(0,255))
            objColor = GeometryColor.ByGeometryColor(geom, color)
            outrect.append([bbxCandiate, objColor])
        i += 1
        pta.Dispose()
        ptd.Dispose()
    return outrect

container = IN[0]

OUT = generator(container, IN[1])
6 Likes

Nice use of bounding boxes. :slight_smile:

1 Like

Now i have 3 option :smiley:
Wow, awesome community here

2 Likes

cool!

2 Likes

Another approach would be to start from the outer rectangle and randomly subdivide in two along the biggest dimension ( vertically or horizontally, with a random radio between 0.3 and 0.7 imho to get good results).

Repeat this step iteratively on the two resulting rectangles. The stopping could be for example if the resulting rectangles areas are smaller than the root’s area multiplied by a given threshold < 1.

Then keep only those last rectangles. The number of final rectangles would be pair but you could change the stopping criterion if this is an issue…

Hey everyone,

I am new to python and especiallywithin the Revit scope.

I watched some videos but for some reason could not get this definition to work and it is the perfect script for what I am doing.

Can somebody help??

I keep getting “IN” expected

@Vikram_Subbaiah do you have any suggestions?

def populateRectangles (srf,count)
{
rectangles = [Imperative]
{
attempts = 0;
results = ;
while (DSCore.List.Count(results) < count && attempts < 10)
{
attempts = attempts +1;

        csSet =
           Surface.CoordinateSystemAtParameter(
                srf,
                Math.RandomList(count),
                Math.RandomList(count)
            );

        rects =
            Rectangle.ByWidthLength(
                Math.RandomList(count/2),
                Math.RandomList(count/2)
            );

        rects =
            Surface.ByPatch(
                Geometry.Transform(
                    rects,
                    csSet
                )
            );
        rects = DSCore.List.Join([results, rects]);

        set = [];
        for (i in0..DSCore.List.Count(rects))
        {
            set =

List.AllFalse(Geometry.DoesIntersect(rects[i],set))?
DSCore.List.Join([set,rects[i]]):
set;

        };

        results = set;
    };

return = results;
rectangles = Geometry.Intersect(rects,srf);
};
rectSet = DSCore.List.Flatten(Geometry.Intersect(rectangles, srf),-1);
return = rectSet[0…count-1];
};

This isn’t working as it isn’t Python but design script. Double click in the canvas to place a code block, and past the code I posted above into the text editor there.

1 Like