// 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();
}
}
Created
October 22, 2024 13:12
-
-
Save MadhukarMoogala/1d9501ab50cc3427ce1668791a1b9329 to your computer and use it in GitHub Desktop.
Working with UCS and Ordinate Dimensions in AutoCAD using .NET API
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment