Skip to content

Instantly share code, notes, and snippets.

@MadhukarMoogala
Created October 22, 2024 13:12
Show Gist options
  • Save MadhukarMoogala/1d9501ab50cc3427ce1668791a1b9329 to your computer and use it in GitHub Desktop.
Save MadhukarMoogala/1d9501ab50cc3427ce1668791a1b9329 to your computer and use it in GitHub Desktop.
Working with UCS and Ordinate Dimensions in AutoCAD using .NET API
    // Tolerance for detecting near-zero values,
    // used to account for floating-point precision errors.
    const double ANGLE_EPSILON = 1E-15;

    // Checks if a given number is near zero, within a defined tolerance.
    public static bool IsNearZero(double num)
    {
        return num < ANGLE_EPSILON && num > -ANGLE_EPSILON;
    }

    // Normalizes an angle to the range [0, 2π], correcting for floating-point precision issues.
    // This handles small imprecisions when dealing with angles across different architectures.
    public static double FixAngle(double angle)
    {
        const double TWOPI = 2 * Math.PI; // Constant representing 2π (full circle in radians)
        double retang;

        if (angle - 1.0 == angle) // Check for infinity
            angle = 0; // Treat infinity as zero for angles

        retang = angle / TWOPI;
        retang = (retang - (int)retang) * TWOPI; // Normalize angle within [0, 2π]

        // Unify close-to-zero negative values to zero
        // to avoid differences across architectures (ARM vs Intel).
        if (retang < 0 && IsNearZero(retang))
        {
            retang = 0.0;
        }

        if (retang < 0.0)
            retang += TWOPI; // Correct for negative angles

        if (retang >= TWOPI)
            retang -= TWOPI; // Correct for angles exceeding 2π

        return retang;
    }

    // Converts an angle in world coordinates to local coordinates
    // based on a given plane (defined by normal and UCS X-direction vectors).
    public static double UserToLocalAngle(Vector3d normal, Vector3d ucsXdir)
    {
        // Transformation matrix from World Coordinate System (WCS) to Local Coordinate System (LCS)
        var wcsToLcs = Matrix3d.WorldToPlane(normal);

        // Transform the UCS X-direction vector into the local coordinate system
        var xDir = ucsXdir;
        xDir.TransformBy(wcsToLcs);

        // Calculate the angle in the local plane using Atan2
        var angle = Math.Atan2(xDir.Y, xDir.X);

        // Normalize the angle using FixAngle to ensure it is within [0, 2π]
        return FixAngle(angle);
    }

    // Command method to test dimensioning of ordinates in AutoCAD
    [CommandMethod("TstDimOrdinate")]
    public static void TestDimOrdinate()
    {
        // Get the active AutoCAD document
        var doc = Application.DocumentManager.MdiActiveDocument;
        if (doc == null) return;

        var db = doc.Database; // Get the database of the active document
        var ed = doc.Editor;   // Get the editor (for user interaction)

        // Start a transaction to modify the database
        using (var tr = db.TransactionManager.StartTransaction())
        {
            // Open the block table and the model space block table record for writing
            var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            var btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

            // Create a new UCS based on specified vectors
            var ucs = new CoordinateSystem3d(Point3d.Origin, new Vector3d(1, 1, 0), new Vector3d(-1, 1, 0));

            // Open the UCS table for reading
            var ucsTbl = (UcsTable)tr.GetObject(db.UcsTableId, OpenMode.ForRead);
            UcsTableRecord ucstr;

            // Check if the UCS named "TestUcs" already exists, if not, create it
            if (!ucsTbl.Has("TestUcs"))
            {
                ucstr = new UcsTableRecord
                {
                    Name = "TestUcs" // Name the UCS "TestUcs"
                };

                // Open the UCS table for writing and add the new UCS
                tr.GetObject(db.UcsTableId, OpenMode.ForWrite);
                ucsTbl.Add(ucstr);
                tr.AddNewlyCreatedDBObject(ucstr, true);
            }
            else
            {
                // Retrieve the existing UCS if "TestUcs" already exists
                ucstr = (UcsTableRecord)tr.GetObject(ucsTbl["TestUcs"], OpenMode.ForWrite);
            }

            // Set UCS origin and axes
            ucstr.Origin = Point3d.Origin;
            ucstr.XAxis = new Vector3d(1, 1, 0);
            ucstr.YAxis = new Vector3d(-1, 1, 0);

            // Get the active viewport and configure the UCS icon to display at the origin
            var vptr = (ViewportTableRecord)tr.GetObject(doc.Editor.ActiveViewportId, OpenMode.ForWrite);
            vptr.IconAtOrigin = true;
            vptr.IconEnabled = true;

            // Set the current UCS to the newly created or retrieved UCS
            vptr.SetUcs(ucstr.ObjectId);
            doc.Editor.UpdateTiledViewportsFromDatabase();

            // Prompt the user to input two points
            PromptPointResult pPtRes;
            PromptPointOptions pPtOpts = new PromptPointOptions("");
            Point3d[] point3Ds = new Point3d[2];

            for (int i = 0; i < 2; i++)
            {
                pPtOpts.Message = $"\nEnter point {i + 1}: ";
                pPtRes = doc.Editor.GetPoint(pPtOpts);

                if (pPtRes.Status != PromptStatus.OK)
                {
                    doc.Editor.WriteMessage("\nPoint input was canceled or invalid.");
                    break;
                }

                point3Ds[i] = pPtRes.Value; // Store the entered points
            }

            // Transform the input points to the current UCS
            Matrix3d activeUCS = ed.CurrentUserCoordinateSystem;
            Point3d ucsDp = point3Ds[0].TransformBy(activeUCS);
            Point3d ucsLp = point3Ds[1].TransformBy(activeUCS);

            // Calculate the angle of rotation for the ordinate dimension
            double rotation = UserToLocalAngle(activeUCS.CoordinateSystem3d.Zaxis, activeUCS.CoordinateSystem3d.Xaxis);

            // Create a new Ordinate Dimension with the calculated properties
            OrdinateDimension ordDim = new OrdinateDimension
            {
                DefiningPoint = ucsDp,
                LeaderEndPoint = ucsLp,
                Normal = activeUCS.CoordinateSystem3d.Xaxis.CrossProduct(activeUCS.CoordinateSystem3d.Yaxis),
                UsingXAxis = false,
                DimensionStyle = db.Dimstyle,
                HorizontalRotation = -rotation,
                ColorIndex = 1

            };

            ordDim.SetDatabaseDefaults();
            btr.AppendEntity(ordDim);
            tr.AddNewlyCreatedDBObject(ordDim, true);

            // Commit the transaction to finalize changes
            tr.Commit();
        }
    }

Video

DIMORDINATE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment