Here’s my contribution to the effort. I made things easy on myself by increasing the level of symmetry, thus avoiding any “roll” axis concerns.
I originally figured a “cylinder length” method would be fairly straight forward but it turned out rather elusive. So much so I can virtually guarantee I missed a better method along the way. Casting a Face's Surface as a Cylinder, which seems appropriate, was a big roadblock.
The only success I could realize was via Face/NurbsSurface conversion. I don’t find that conversion particularly dreadful (as I have done some programming for Rhino) but can see how it would be distasteful to others.
In any event, the code is below. It only processes one solid at a time but will probably suffer the same level of inconsistency - as mentioned previously in this thread - for larger selection sets.
using System;
using System.Text;
using System.Collections.Generic;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.BoundaryRepresentation;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using System.Runtime.InteropServices;
using AcAp = Autodesk.AutoCAD.ApplicationServices;
using AcEd = Autodesk.AutoCAD.EditorInput;
using AcGe = Autodesk.AutoCAD.Geometry;
using AcRx = Autodesk.AutoCAD.Runtime;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcWd = Autodesk.AutoCAD.Windows;
using AcBr = Autodesk.AutoCAD.BoundaryRepresentation;
[assembly: CommandClass(typeof(CsMgdAcad5.STSCCommands))]
namespace CsMgdAcad5
{
public class STSCCommands
{
/// <summary>
/// Orient cylinder to return usefull BB info
/// </summary>
public STSCCommands()
{
//
// TODO: Add constructor logic here
//
}
// Define Command "CylinderSize"
[CommandMethod("CylSize")]
static public void CylinderDimensions() // This method can have any name
{
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Transaction tr = db.TransactionManager.StartTransaction();
try
{
// Prompt for selection of a solid
PromptEntityOptions prEntOpt = new PromptEntityOptions("Select a 3D solid: ");
prEntOpt.SetRejectMessage("\nPlease select only a 3D Solid");
prEntOpt.AddAllowedClass(typeof(Solid3d), true);
PromptEntityResult prEntRes = ed.GetEntity(prEntOpt);
ObjectId[] objIds = { prEntRes.ObjectId };
Solid3d sol = (Solid3d)tr.GetObject(prEntRes.ObjectId, OpenMode.ForRead);
Brep brp = new Brep(sol);
int cmplxCount = 0, shllCount = 0;
foreach (Complex cmplx in brp.Complexes)
{
++cmplxCount;
}
if (cmplxCount == 1)
{
List<AcBr.Complex> lstCmPlx = new List<AcBr.Complex>();
lstCmPlx.AddRange(brp.Complexes);
foreach (Shell shll in lstCmPlx[0].Shells)
{
++shllCount;
}
if (shllCount == 1)
{
List<AcBr.Shell> lstShell = new List<AcBr.Shell>();
lstShell.AddRange(brp.Shells);
double LargestArea = 0.0;
Autodesk.AutoCAD.BoundaryRepresentation.Face FaceLarge = null;
foreach (Autodesk.AutoCAD.BoundaryRepresentation.Face fce in lstShell[0].Faces)
//Let's assume the Face with the largest area defines extrusion direction.
//This is certainly not a bullet proof assumption but will work for the time being
{
if (fce.GetArea() > LargestArea)
{
FaceLarge = fce;
LargestArea = FaceLarge.GetArea();
}
}
NurbSurface NS = FaceLarge.GetSurfaceAsNurb();
if (NS.IsPeriodicInU ^ NS.IsPeriodicInV)//Locate cylindrical like surface (needs work to exclude Cone and Torus)
{
NurbSurfaceDefinition NSD = NS.GetDefinition();
Point3dCollection P3C = NSD.ControlPoints;
int CPCount = P3C.Count;
int CPCountU = NS.NumControlPointsInU;
int CPCountV = NS.NumControlPointsInV;
Point3d dir1;
Point3d dir2;
if (NS.IsPeriodicInU)
{
dir1 = P3C[0];
dir2 = P3C[CPCount / CPCountV];
}
else
{
dir1 = P3C[0];
dir2 = P3C[CPCount / CPCountU];
}
Vector3d DirV = (dir2.Subtract(dir1.GetAsVector())).GetAsVector();
Matrix3d mat = Matrix3d.WorldToPlane(DirV);
mat = mat.Inverse();
mat = mat.Transpose();
sol.UpgradeOpen();
sol.TransformBy(mat);
dir1 = sol.GeometricExtents.MinPoint;
dir2 = sol.GeometricExtents.MaxPoint;
double X = Math.Round(Math.Abs(dir1.X - dir2.X),6);
double Y = Math.Round(Math.Abs(dir1.Y - dir2.Y),6);
double Z = Math.Round(Math.Abs(dir1.Z - dir2.Z),6);
StringBuilder sb = new StringBuilder();
sb.Append("(");
sb.Append(X.ToString());
sb.Append(",");
sb.Append(Y.ToString());
sb.Append(",");
sb.Append(Z.ToString());
sb.Append(")");
ed.WriteMessage("\n" + sb.ToString());
}
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage("\nException in traversal: {0}", ex.Message);
}
finally
{
tr.Dispose();
}
}
}
}