Rhino3dmIO, wrong bounding boxes from annotation objects


I need bound boxes from all document objects. When got from curves they are correct, when got from annotations they are wrong, but not too much. Example:

The correct bounding box is: x0,y0= 10.13, 4.96 x1,y1= 17.70, 9
The reported bound box is: x0,y0= 10.13, 0.96 x1,y1= 17.70, 5

If I use as bounding box origin the annotation plane origin and keep the dx and dy above I get a better, but still wrong, result:

x0,y0= 10, 5 x1,y1= 17.57, 9.04

I’m using Rhino3dmIO v 6.27.20176.5001 under Visual Studio 2015. I read them like this: Rhino.Geometry.BoundingBox bb = o.Geometry.GetBoundingBox(true);

Any idea? Thanks. L

@lowell - is this something you can help with?

Please post a simple example 3dm file and script so I can see clearly what’s up.


here it is:

using System;
using Rhino.FileIO;
namespace gridExtractor
    class Program
        static void Main(string[] args)
            File3dm rhinoFile = File3dm.Read(@"C:\Users\lscandella\data\gucci\Rhinoceros\gridExtractor\gridExtractor\txt.3dm");
            File3dmObjectTable objectTable = rhinoFile.Objects;
            foreach (File3dmObject o in objectTable)
                Rhino.Geometry.BoundingBox bb = o.Geometry.GetBoundingBox(true);
                Console.WriteLine("Bounding box x0,y0,x1,y1: " + bb.Min.X.ToString() + " " + bb.Min.Y.ToString() + " " + bb.Max.X.ToString() + " " + bb.Max.Y.ToString());
            Console.WriteLine("Press any key to stop...");

Please rename the path to point to the attached filetxt.3dm (21.8 KB)

and forget my ideas about the annotation plane. The problem is text formatting. Apparently the boundingBox method does not care about it. In the example file you find a simple string “Txt” aligned on the left and on the bottom. The method reports it bounding box as if it were aligned on the left and on the top.

Thanks. L

@lscandella - Thanks for the report - There’s a bug in the dotnet interface for annotation bounding box.


is the C++ lib OK?
Thanks. L

Yes, the bug had to do with the rhino common wrapper not getting the right dimstyle

RH-59566 is fixed in the latest Service Release Candidate


I’m confused. Isn’t the bug in the Rhino3dmIO library? How can a Rhino 6 update fix it?

Thanks. L

Hi @lscandella,

When SR29 is released there will be an SR29 build of Rhino3dmIO available on NuGet.

– Dale

Hi @dale @lowell @brian,

Having a similar problem with sphere and torus. The bounding box doesn’t fit the object. Here is a sample file and the resulting bbox I get using Rhino3dmIO 6.28 nuget package. The program I used is attached at the bottom.

1.3dm (9.3 KB) : Sphere geometry
1.bbox.3dm (14.4 KB) : Resulting bbox using Rhino3dmIO

    static void Main(string[] args)
        var outputPath = Path.ChangeExtension(args[0],"bbox.3dm");
        using(var file3dm = File3dm.Read(args[0]))
            var outputFile = new File3dm();
            foreach(var rhinoObject in file3dm.Objects)

Hi @mpcarlos87,

Tight bounding boxes for Breps are computed in Rhino from their render mesh. Rhino3dmIO is not capable of tessellating Breps. So this is what you are going to get.

If a tight bounding box for Breps is important to you in Rhino3dmIO, then I recommend saving 3dm files with cached render meshes. Then, then you need the tight bounding boxes of Breps, get the meshes from the Brep faces and build a bounding box based on them.

Hope this helps.

– Dale


thanks, but my client can’t wait. Here is my workaround:

        public myBoundingBox2d getTextEntityBoundingBox(File3dmObject o)
            Rhino.Geometry.BoundingBox bb = o.Geometry.GetBoundingBox(true);
            myBoundingBox2d mbb = new myBoundingBox2d(bb.Min.X, bb.Min.Y, bb.Max.X, bb.Max.Y);
            Rhino.Geometry.AnnotationBase ab = (Rhino.Geometry.AnnotationBase)o.Geometry;
            myPoint2d origin = appDocument.getPoint(ab.Plane.Origin);
            Rhino.Geometry.TextEntity te = (Rhino.Geometry.TextEntity)ab;
            List<Rhino.Geometry.TextJustification> jRight = new List<Rhino.Geometry.TextJustification>();
            List<Rhino.Geometry.TextJustification> jCenter = new List<Rhino.Geometry.TextJustification>();
            List<Rhino.Geometry.TextJustification> jBottom = new List<Rhino.Geometry.TextJustification>();
            List<Rhino.Geometry.TextJustification> jMiddle = new List<Rhino.Geometry.TextJustification>();
            myVector2d jv = new myVector2d(0, 0);
            if (jRight.Contains(te.Justification)) jv.x = -1;
            if (jCenter.Contains(te.Justification)) jv.x = -.5;
            if (jBottom.Contains(te.Justification)) jv.y = 1;
            if (jMiddle.Contains(te.Justification)) jv.y = .5;
            double textOrientationRads = ab.TextRotationRadians;
            ab.TextRotationRadians = 0;
            double scale = ab.DimensionScale;
            myVector2d planeXAxis = new myVector2d(ab.Plane.XAxis.X, ab.Plane.XAxis.Y);
            double theta = Math.Atan2(planeXAxis.y, planeXAxis.x);
            ab.Rotate(-theta, new Rhino.Geometry.Vector3d(0, 0, 1), ab.Plane.Origin);
            bb = ab.GetBoundingBox(true);
            string originalText = ab.PlainText;
            List<string> originalLInes = myStaticClass.stringToLines(originalText);
            if (originalLInes.Count > 1) // lines can't be empty
                string fakeText = "";
                for (int r = 0; r < originalLInes.Count; r++)
                    string line = originalLInes[r];
                    if (line == "") line = "H";
                    fakeText += line;
                    if (r != originalLInes.Count - 1) fakeText += "\r\n";
                ab.PlainText = fakeText;
                bb = ab.GetBoundingBox(true);
                ab.PlainText = originalText;
            mbb = new myBoundingBox2d(bb.Min.X, bb.Max.Y - scale * (bb.Max.Y - bb.Min.Y), bb.Min.X + scale * (bb.Max.X - bb.Min.X), bb.Max.Y);
            myPolyline2d hBox = mbb.getPolyline2d();
            myBoundingBox2d hBoxBB = hBox.getBounds();
            double dx = hBoxBB.getDeltaX();
            double dy = hBoxBB.getDeltaY();
            jv.x = jv.x * dx;
            jv.y = jv.y * dy;
            myPolyline2d tBox = new myPolyline2d(hBox);
            myPolyline2d rBox = new myPolyline2d(tBox);
            rBox.rotateAboutPoint(theta + textOrientationRads, origin);
            return (rBox.getBounds());

What my classes myBoundingBox2d and myPolyline2d do should be straightforward

Hi @dale, thanks for your response but from what I’ve tested there is something that doesn’t fit.

I attach here 2 spheres with same radius in the same place and the bounding box I get is different. Both spheres were exported as only geometry.

1.3dm (9.3 KB) 1.bbox.3dm (14.4 KB)
2.3dm (10.2 KB) 2.bbox.3dm (14.4 KB)

You can use the same program I’ve attached above to get the bounding box.


Hi @mpcarlos87,

I am guessing that one of these files has once been shaded before being saved as geometry-only, in which case the ON_Brep::m_bbox member would be valid and, thus, returned if one asked for it.

– Dale

Hi @dale I’ve noticed the spheres return different bounding box depending on the rotation they have and maybe the one fit in my sample was tighter just by luck? Check this video

Do you have plans to fix it?


Hi mpcarlos87,

As @dale pointed out, tight boundingboxes cannot be computed when the object lacks a rendermesh attached to it. It looks to me that instead the ‘controlpoints’ for the (sub)surfaces are used.

If you rotate the sphere, its controlpoints are oriented dirrerent in relation to the world plane thus you get a different result for the boundingbox:

@dale might be able to confirm of deny this assumption

Does this make sense?

Hi @Willem,

Yes, I think that fits what I was getting as results!

Thanks :slight_smile: