How to create dimension on Solid3D of Edge

genosyde
Advocate
Advocate

How to create dimension on Solid3D of Edge

genosyde
Advocate
Advocate

Hello dear developers.
I'm currently working on a feature that adds dimensions to 3D Solid.

genosyde_0-1632546492196.png

That red line is the CAD UI function after changing the UCS. I'm trying to make it a CAD API.

The yellow dimension is the dimension created with the API.

As you can see the dimensions are messed up.

 

I am still stuck because I do not know how to use UCS as API Code.
Please help.

 

As an aside,
I hope this work will help you get to know UCS.
Are there any sites where I can study UCS and spatial coordinates in various ways?

 

[CommandMethod("DIMADD")]
public static void DIMADD()
{
    editor = acap.DocumentManager.MdiActiveDocument.Editor;

    // Get the current document and database, and start a transaction
    Document doc = acap.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // select a line
    PromptEntityOptions options = new PromptEntityOptions(
                    "\nSelect an Solid3D Entity: ");
    options.SetRejectMessage("\nSelect Solid3D");
    options.AddAllowedClass(typeof(Solid3d), false);
    PromptEntityResult res = editor.GetEntity(options);

    // if ok
    if (res.Status != PromptStatus.OK)
        return;


    double width = 1600;
    double height = 800;

    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        BlockTable btl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord btr = null;

        // Open the Block table record Model space for write
        BlockTableRecord ms;
        ms = tr.GetObject(btl[BlockTableRecord.ModelSpace],
        OpenMode.ForWrite) as BlockTableRecord;

        //  select Solid
        Entity ent = (Entity)tr.GetObject(res.ObjectId, OpenMode.ForRead);
        if (ent.GetType() != typeof(Solid3d)) { editor.WriteMessage("It's not Solid3d."); return; }
        Solid3d solid = ent as Solid3d;


        using (Brep brep = new Brep(solid))
        {
            BrepEdgeCollection edges = brep.Edges;

            foreach (Edge edge in edges)
            {
                Curve3d curve = ((ExternalCurve3d)edge.Curve).NativeCurve;

                if (curve is LineSegment3d)
                {
                    LineSegment3d line = curve as LineSegment3d;

                    Point3d dimpt1 = curve.StartPoint;
                    Point3d dimpt2 = curve.EndPoint;
                    Point3d dimcpt = CADUtil.GetCenterPts(new Point3d[] { dimpt1, dimpt2 });
                    dimcpt = new Point3d(dimcpt.X, dimcpt.Y + 200, dimcpt.Z);

                    //  Here, i don't know how to apply ucs to my dimension point3d.
                    {

                    }


                    //  Dimension
                    RotatedDimension acRotDim = new RotatedDimension();
                    acRotDim.SetDatabaseDefaults();
                    acRotDim.XLine1Point = dimpt1;
                    acRotDim.XLine2Point = dimpt2;
                    //acRotDim.Rotation = line.Angle;
                    acRotDim.DimLinePoint = dimcpt;
                    acRotDim.Layer = "0";
                    acRotDim.Dimtxt = 50;
                    acRotDim.Dimtad = 1;
                    ms.AppendEntity(acRotDim);
                    tr.AddNewlyCreatedDBObject(acRotDim, true);
                }
            }
        }

        tr.Commit();
    }
}

 

Thanks for reading, have a good weekend. 

0 Likes
Reply
Accepted solutions (2)
765 Views
8 Replies
Replies (8)

genosyde
Advocate
Advocate

I forgot to attach the autocad file.
The DWG file is also attached.

0 Likes

_gile
Mentor
Mentor

Hi,

The UCS is mainly provided for the final user as drawing helper.

While driving AutocCAD by code, we typically prefer using Matrix3d instance with the TransformBy and/or the geometrical properties of some 2d entities.
The abstract Dimension class provides the Elevation and Normal properties which should be all you need to set the dimensions position and orientation.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

genosyde
Advocate
Advocate

Thanks for your reply.
I'm new to abstract dimensions.

genosyde_0-1632562953525.png

As shown in the picture, linear or radial .... I don't think it's the same type of dimension.

Currently, I found a hint in an answer you posted to someone else before.

https://forums.autodesk.com/t5/net/ucs-command-programatically/m-p/8474610

 

Currently, I think you found a hint in an answer you posted to someone else before.
You gave your answer using a Plane you created separately in your previous answer.
This was exactly the answer I was looking for.
So, I am currently looking for a way to extract a Plane from PolyFaceMesh.

PolyFaceMesh pfm = (PolyFaceMesh)ent;
Point3dCollection pfmpts = new Point3dCollection();
FaceRecord fr;
Plane originPlane = null;
foreach (ObjectId id in pfm)
{
    DBObject obj = tr.GetObject(id, OpenMode.ForRead);
    if (obj is PolyFaceMeshVertex)
    {
        PolyFaceMeshVertex vertex = (PolyFaceMeshVertex)obj;
        pfmpts.Add(vertex.Position);
    }
    else if (obj is FaceRecord)
    {
        fr = (FaceRecord)obj;

        //  This line occured eNotApplicable Error
        originPlane = fr.GetPlane();
    }
}

 

Of course, using the abstract dimensions you suggested would be fantastic. If I can find it...

Excuse me, but can you share some information about abstract figures?

 

0 Likes

_gile
Mentor
Mentor
Accepted solution

Sorry if I wasn't clear enough.

I should have said: "The abstract Dimension class, which is the base class for all Dimension types, provides the Elevation and Normal properties..." That means all Dimension types (AlignedDimension, RotatedDimension, and so on) have these properties.

In your first example, with a 'box' solid orthogonally oriented, using Normal and Elevation would be a quite easy way to create dimensions in the desired plane.

 

Now you seems to talk about PolyFaceMesh and this won't be not as trivial at all.

A Face of PolyFaceMech might contain more then 3 vertices, that means the Face might not be planar.

If the face only have 3 vertices, you can follow the example of the topic you linked.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

genosyde
Advocate
Advocate

Dear _gile.

Thanks for your kind reply.
I've done enough tests for the 'Elevation' and 'Normal' properties you mentioned.
I am also studying Normal's graphics theory.
But it seems quite different from the 3D programs I've used so far. such as Autodesk 3D Max or Autodesk SoftImage.

As you mentioned, I tested Elevation and Normal with various values.

 

genosyde_0-1632651164631.png

However, the results are not good.

The Elevation value is entered correctly, but the axis of Dimension appears wrong.

genosyde_1-1632651319047.png

Since it is a test code, I'm testing only with Top Face to reduce complexity as much as possible.

Could you please fix the problem in my code?


I want a result like the image below.

genosyde_2-1632651658392.png

 

ETC.

The 6 spheres in the image represent the center of each face.
There is no other special meaning.

 

 

 

 

[CommandMethod("DIMADD")]
#region command:DIMADD
public static void DIMADD()
{
    editor = acap.DocumentManager.MdiActiveDocument.Editor;

    // Get the current document and database, and start a transaction
    Document doc = acap.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // select a line
    PromptEntityOptions options = new PromptEntityOptions(
                    "\nSelect an Solid3D Entity: ");
    options.SetRejectMessage("\nSelect Solid3D");
    options.AddAllowedClass(typeof(Solid3d), false);
    PromptEntityResult res = editor.GetEntity(options);

    // if ok
    if (res.Status != PromptStatus.OK)
        return;


    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        BlockTable btl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord btr = null;

        // Open the Block table record Model space for write
        BlockTableRecord ms;
        ms = tr.GetObject(btl[BlockTableRecord.ModelSpace],
        OpenMode.ForWrite) as BlockTableRecord;


        //  select Solid
        Entity ent = (Entity)tr.GetObject(res.ObjectId, OpenMode.ForRead);
        if (ent.GetType() != typeof(Solid3d)) { editor.WriteMessage("It's not Solid3d."); return; }
        Solid3d solid = ent as Solid3d;

        using (Brep brep = new Brep(solid))
        {
            Point3d resultpt = Point3d.Origin;
            Vector3d resultvect = Vector3d.ZAxis;
            BrepFaceCollection faces = brep.Faces;
            foreach (Autodesk.AutoCAD.BoundaryRepresentation.Face face in faces)
            {
                var geomNurb = face.GetSurfaceAsNurb();
                var weights = new DoubleCollection();
                try { weights = geomNurb.Weights; }
                catch { }
                using(var dbNurb = new Autodesk.AutoCAD.DatabaseServices.NurbSurface(
                    geomNurb.DegreeInU,
                    geomNurb.DegreeInV,
                    geomNurb.IsRationalInU && geomNurb.IsRationalInV,
                    geomNurb.NumControlPointsInU,
                    geomNurb.NumControlPointsInV,
                    geomNurb.ControlPoints,
                    weights,
                    geomNurb.UKnots,
                    geomNurb.VKnots))
                {
                    dbNurb.IsPlanar(out Point3d point, out Vector3d normal);
                    resultpt = point; resultvect = normal;
                    editor.WriteMessage($"\r\ncpt : {point}   normal : {normal}");
                }

                if(face.Loops != null)
                {
                    foreach (var loop in face.Loops)
                    {
                        if(loop.Edges != null)
                        {
                            foreach (var edge in loop.Edges)
                            {
                                Curve3d curve = ((ExternalCurve3d)edge.Curve).NativeCurve;
                                if(curve is LineSegment3d)
                                {
                                    LineSegment3d line = curve as LineSegment3d;

                                    Point3d dimpt1 = curve.StartPoint;
                                    Point3d dimpt2 = curve.EndPoint;
                                    Point3d dimcpt = CADUtil.GetCenterPts(new Point3d[] { dimpt1, dimpt2 });
                                    dimcpt = new Point3d(dimcpt.X, dimcpt.Y, dimcpt.Z);

                                    #region failed:input vector directly
                                    //Vector3d vec1 = new Vector3d(0, 0, 1);
                                    //Vector3d vec2 = new Vector3d(0, 0, -1);
                                    //Vector3d vec3 = new Vector3d(1, 0, 0);
                                    //Vector3d vec4 = new Vector3d(-1, 0, 0);
                                    //Vector3d vec5 = new Vector3d(0, 1, 0);
                                    //Vector3d vec6 = new Vector3d(0, -1, 0);
                                    #endregion

                                    Line vline = new Line(dimpt1, new Point3d(dimpt1.X, dimpt1.Y , dimpt1.Z + 200));
                                    Vector3d vect1 = dimpt1.GetVectorTo(dimpt2);
                                    Vector3d vect2 = dimpt1.GetVectorTo(vline.EndPoint);
                                    var norm = vect1.CrossProduct(vect2);
                                    var plane = new Plane(dimcpt, norm);
                                    var xform = Matrix3d.WorldToPlane(plane);


                                    //  Dimension
                                    RotatedDimension acRotDim = new RotatedDimension();
                                    acRotDim.SetDatabaseDefaults();
                                    acRotDim.XLine1Point = dimpt1.TransformBy(xform);
                                    acRotDim.XLine2Point = dimpt2.TransformBy(xform);
                                    //acRotDim.DimLinePoint = dimcpt.TransformBy(xform);
                                    acRotDim.Elevation = 200;
                                    acRotDim.Normal = norm;
                                    acRotDim.Layer = "0";
                                    acRotDim.Dimtxt = 50;
                                    acRotDim.Dimtad = 1;
                                    ms.AppendEntity(acRotDim);
                                    tr.AddNewlyCreatedDBObject(acRotDim, true);

                                    acRotDim.TransformBy(Matrix3d.PlaneToWorld(plane));
                                }
                            }
                        }
                    }
                }

                //  only runs at Top face and then break.
                break;
            }
        }

        tr.Commit();
    }
}
#endregion

 

 

 

 

 

 

0 Likes

genosyde
Advocate
Advocate
 

dear _gile
Thanks to you, I was able to understand Normal.
I solved the problem by referring to the comments you wrote in 2014.
https://forums.autodesk.com/t5/net/dimension-in-3d-using-vb-net/td-p/5278641https://forums.autodesk....

 

genosyde_0-1632676451477.png

I tested it with the code you posted in that link, and I was able to understand it from the comments saying that it is parallel to XZ Plane.

Hope this solution helps someone else
I upload my code.

Once again, I sincerely thank _gile.
Have a restful night.

 

 

[CommandMethod("DIMADD")]
#region command:DIMADD
public static void DIMADD()
{
    editor = acap.DocumentManager.MdiActiveDocument.Editor;

    // Get the current document and database, and start a transaction
    Document doc = acap.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // select a line
    PromptEntityOptions options = new PromptEntityOptions(
                    "\nSelect an Solid3D Entity: ");
    options.SetRejectMessage("\nSelect Solid3D");
    options.AddAllowedClass(typeof(Solid3d), false);
    PromptEntityResult res = editor.GetEntity(options);

    // if ok
    if (res.Status != PromptStatus.OK)
        return;


    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        BlockTable btl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord btr = null;

        // Open the Block table record Model space for write
        BlockTableRecord ms;
        ms = tr.GetObject(btl[BlockTableRecord.ModelSpace],
        OpenMode.ForWrite) as BlockTableRecord;


        //  select Solid
        Entity ent = (Entity)tr.GetObject(res.ObjectId, OpenMode.ForRead);
        if (ent.GetType() != typeof(Solid3d)) { editor.WriteMessage("It's not Solid3d."); return; }
        Solid3d solid = ent as Solid3d;

        using (Brep brep = new Brep(solid))
        {
            //  Open faces of selected solid.
            Point3d resultpt = Point3d.Origin;
            Vector3d resultvect = Vector3d.ZAxis;
            BrepFaceCollection faces = brep.Faces;
            foreach (Autodesk.AutoCAD.BoundaryRepresentation.Face face in faces)
            {
                var geomNurb = face.GetSurfaceAsNurb();
                var weights = new DoubleCollection();
                try { weights = geomNurb.Weights; }
                catch { }
                using(var dbNurb = new Autodesk.AutoCAD.DatabaseServices.NurbSurface(
                    geomNurb.DegreeInU,
                    geomNurb.DegreeInV,
                    geomNurb.IsRationalInU && geomNurb.IsRationalInV,
                    geomNurb.NumControlPointsInU,
                    geomNurb.NumControlPointsInV,
                    geomNurb.ControlPoints,
                    weights,
                    geomNurb.UKnots,
                    geomNurb.VKnots))
                {
                    dbNurb.IsPlanar(out Point3d point, out Vector3d normal);
                    resultpt = point; resultvect = normal;
                    editor.WriteMessage($"\r\ncpt : {point}   normal : {normal}");
                }

                //  Open edges of faces
                if(face.Loops != null)
                {
                    foreach (var loop in face.Loops)
                    {
                        if(loop.Edges != null)
                        {
                            foreach (var edge in loop.Edges)
                            {
                                Curve3d curve = ((ExternalCurve3d)edge.Curve).NativeCurve;
                                if(curve is LineSegment3d)
                                {
                                    //  horizontal line for X axis
                                    LineSegment3d line = curve as LineSegment3d;
                                    Point3d dimpt1 = curve.StartPoint;
                                    Point3d dimpt2 = curve.EndPoint;
                                    Point3d dimcpt = CADUtil.GetCenterPts(new Point3d[] { dimpt1, dimpt2 });
                                    dimcpt = new Point3d(dimcpt.X, dimcpt.Y, dimcpt.Z + 200);

                                    //  create vertical line for z axis
                                    Line vline = new Line(dimpt1, new Point3d(dimpt1.X, dimpt1.Y , dimpt1.Z + 200));
                                    Vector3d vect1 = dimpt1.GetVectorTo(dimpt2).GetNormal();

                                    //  the top face can use two normals.  (0, 1, 0) = XZ plane,   (1, 0, 0) = YZ plane.
                                    Vector3d vecXdir = new Vector3d(1, 0, 0);
                                    Vector3d vecYdir = new Vector3d(0, 1, 0);

                                    //  parallels check.
                                    //if(vecXdir.IsParallelTo(vect1))
                                    if (vecXdir == vect1 || vecXdir == vect1.Negate())
                                        norm = new Vector3d(0, 1, 0);
                                    //else if(vecYdir.IsParallelTo(vect1))
                                    else if (vecYdir == vect1 || vecYdir == vect1.Negate())
                                        norm = new Vector3d(1, 0, 0);

                                    var plane = new Plane(dimcpt, norm);
                                    var xform = Matrix3d.WorldToPlane(plane);
                                    RotatedDimension acRotDim = new RotatedDimension();
                                    acRotDim.SetDatabaseDefaults();
                                    acRotDim.XLine1Point = dimpt1.TransformBy(xform);
                                    acRotDim.XLine2Point = dimpt2.TransformBy(xform);
                                    acRotDim.DimLinePoint = dimcpt.TransformBy(xform);

                                    //  Since the plane already has the normal vector, adding the normal here messed up with the double input
                                    //acRotDim.Normal = new Vector3d(0, 1, 0);
                                    acRotDim.Layer = "0";
                                    acRotDim.Dimtxt = 100;
                                    acRotDim.Dimtad = 1;
                                    ms.AppendEntity(acRotDim);
                                    tr.AddNewlyCreatedDBObject(acRotDim, true);

                                    acRotDim.TransformBy(Matrix3d.PlaneToWorld(plane));
                                }
                            }
                        }
                    }
                }

                //  only runs at Top face and then break.
                break;
            }
        }
        tr.Commit();
    }
}
#endregion

 

0 Likes

_gile
Mentor
Mentor
Accepted solution

Here's an example.

        [CommandMethod("TEST")]
        public static void Test()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var peo = new PromptEntityOptions("\nSelect Solid 3d: ");
            peo.SetRejectMessage("\nSelected object is not a Solid 3d.");
            peo.AddAllowedClass(typeof(Solid3d), false);
            var per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK) 
                return;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                var solid = (Solid3d)tr.GetObject(per.ObjectId, OpenMode.ForRead);
                using (var brep = new Brep(solid))
                {
                    // get the top Edges
                    var topEdges = brep.Edges
                        .Select(e => ((ExternalCurve3d)e.Curve).NativeCurve)
                        .Where(c => c is LineSegment3d && c.StartPoint.Z == c.EndPoint.Z)
                        .Cast<LineSegment3d>()
                        .GroupBy(l => l.StartPoint.Z)
                        .OrderByDescending(g => g.Key)
                        .First();
                    foreach (var line in topEdges)
                    {
                        if (line.Direction.IsParallelTo(Vector3d.XAxis))
                        {
                            var dim = new RotatedDimension(0.0, line.StartPoint, line.EndPoint, line.StartPoint + Vector3d.ZAxis * 200.0, "", db.Dimstyle);
                            dim.Normal = Vector3d.YAxis;
                            dim.Elevation = line.StartPoint.Y;
                            curSpace.AppendEntity(dim);
                            tr.AddNewlyCreatedDBObject(dim, true);
                        }
                        if (line.Direction.IsParallelTo(Vector3d.YAxis))
                        {
                            var dim = new RotatedDimension(0.0, line.StartPoint, line.EndPoint, line.StartPoint + Vector3d.ZAxis * 200.0, "", db.Dimstyle);
                            dim.Normal = Vector3d.XAxis;
                            dim.Elevation = line.StartPoint.X;
                            curSpace.AppendEntity(dim);
                            tr.AddNewlyCreatedDBObject(dim, true);
                        }
                    }
                }
                    tr.Commit();
            }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

genosyde
Advocate
Advocate

Thank you again for your favor.
Your last answer taught me another lesson.
I like to use EF to simplify my code,
In particular, I was impressed with the Conditions of WHERE that extracts only the edge of the top.

 

"if(line.Direction.IsParallelTo(Vector3d.XAxis))"
If I had implemented this part in code, it would have been more than 6 lines,
I clicked the like button because it was neatly and clearly implemented in one line.

I am learning well.
I'd like to post the next question soon.
Have a nice day today. ^^

0 Likes