Fast packing of breps into a predefined rectangle

I am looking for a simple algorithm to pack <50 breps/mesh into a predefined rectangle. The goal is to have a fast tool not an good output. I would be all right to use the boundingboxes of the objects and it is really no problem to have some/much freespace. It is only important to have no overlapping objects.

I only find overcomplex grasshoper files im not able to transfer to c#.

Some hints?

For my tool Wordle in Nautilus that is very similar as packing I choose to use sort of pixel indeed array of bool. So each shape is an array of bool. I will surely implement soon a packing tool so it will be usable in any C# by referencing the library (LDLIB.dll).

But it seems to me as your problem is quite common you could use an existing project that do that. Something like, as it is C# most of the time the only job to do is adding some interface from and to Rhinocommon

Thankyou RectpackSharp was all i need.

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {

            try
            {
                double resize_factor = 1000.0;
                var count = 200;
                uint area_height = Convert.ToUInt32(50 * resize_factor);
                uint area_width = Convert.ToUInt32(100 * resize_factor);
                var objects = GenerateTestObjects(count);
                PackingRectangle[] rectangles = new PackingRectangle[objects.Count];
                Dictionary<int, Sphere> obj_mapper = new Dictionary<int, Sphere>();
                for (int i = 0; i < objects.Count; i++)
                {
                    var obj = objects[i];
                    BoundingBox bb = obj.BoundingBox;
                    var corners = bb.GetCorners();
                    var bb_width = bb.Max.X - bb.Min.X;
                    var bb_height = bb.Max.Y - bb.Min.Y;
                    RhinoApp.WriteLine($"BB = x: {bb_width}, y: {bb_height}");
                    var width = Convert.ToUInt32(Math.Round(bb.Max.X - bb.Min.X, 3) * resize_factor);
                    var height = Convert.ToUInt32(Math.Round(bb.Max.Y - bb.Min.Y, 3) * resize_factor);
                    RhinoApp.WriteLine($"uintBB = x: {width}, y: {height}");
                    PackingRectangle packingRectangle = new PackingRectangle(0, 0, width, height, i);
                    RhinoApp.WriteLine($"packingRectangle = width: {packingRectangle.Width}, height: {packingRectangle.Height}, id: {packingRectangle.Id}");
                    rectangles[i] = packingRectangle;
                    obj_mapper.Add(i, obj);
                }
                RhinoApp.WriteLine($"rectangles = {rectangles.Length}");
                RectanglePacker.Pack(rectangles, out PackingRectangle bounds, PackingHints.FindBest, 1.0, 1, null, area_height);
                RhinoApp.WriteLine($"bounds = width: {Convert.ToDouble(bounds.Width) / resize_factor}, height: {Convert.ToDouble(bounds.Height) / resize_factor}");
                foreach (var rect in rectangles)
                {
                    var rhino_rec = new Rectangle3d(Rhino.Geometry.Plane.WorldXY, Convert.ToDouble(rect.Width) / resize_factor, Convert.ToDouble(rect.Height) / resize_factor);
                    Vector3d move_x = new Point3d(rect.X / resize_factor, 0, 0) - Point3d.Origin;
                    Vector3d move_y = new Point3d(0, rect.Y / resize_factor, 0) - Point3d.Origin;
                    var xform1 = Transform.Translation(move_x);
                    var xform2 = Transform.Translation(move_y);
                    rhino_rec.Transform(xform1);
                    rhino_rec.Transform(xform2);
                    var original_obj = obj_mapper[rect.Id];
                    Vector3d move_xy = rhino_rec.Center - original_obj.BoundingBox.Center;
                    original_obj.Translate(move_xy);
                    doc.Objects.AddCurve(rhino_rec.ToNurbsCurve());
                    doc.Objects.AddSphere(original_obj);
                }
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine(ex.Message);
            }
            return Result.Success;

        }

        private List<Sphere> GenerateTestObjects(int count)
        {
            List<Sphere> spheres = new List<Sphere>();
            for (int i = 0; i < count; i++)
            {
                var radius = GetRandomNumber(1.0, 10.0);
                spheres.Add(new Sphere(Point3d.Origin, radius));
            }
            return spheres;
        }

        private double GetRandomNumber(double minimum, double maximum)
        {
            Random random = new Random((int)DateTime.Now.Ticks);
            return random.NextDouble() * (maximum - minimum) + minimum;
        }

This is my testcode with is creating this sample in 2-3 seconds:

I think the time could be speedup a lot using threading and dont post debug output to the rhinoconsole.

4 Likes

Thanks for showing what you get. It seems a powerful packing tool for rectangles.

Without the prints to Rhinoconsole it is nearly instandly. Really fast.

I will add it to my plugin, here 10000 ellipses in 143 ms!
I will add width or height to the default bin ratio of 1.



Nautilus version 1.9.3 contains this component

4 Likes