Add Text aligned to selected Lines

emmanuel_paniaguaEJTWE
Participant
Participant

Add Text aligned to selected Lines

emmanuel_paniaguaEJTWE
Participant
Participant

this should be quite simple, but i can not find the way to add a Text for each selected Line, so that the Text gets aligned to the center of the line, even if the lines change in the Z axis

 

Until now i have this

public void PrintLinesLenght()
        {
            Database db = doc.Database;
            Editor ed = doc.Editor;
            try
            {
                using (Transaction trans = db.TransactionManager.StartTransaction())
                {
                    ed.WriteMessage("Selecting Lines SPR... ");

                    //set the filter with two criteria: Line and LayerName
                    TypedValue[] typedValues = new TypedValue[2];
                    typedValues.SetValue(new TypedValue((int)DxfCode.Start, "LINE"), 0); //index 0 in array
                    typedValues.SetValue(new TypedValue((int)DxfCode.LayerName, "SPR"), 1); //index 1

                    //assign the filter criteria to a selection filter object
                    SelectionFilter filter = new SelectionFilter(typedValues);

                    //request for objects to be selected by the user 
                    PromptSelectionResult psr;
                    psr = ed.GetSelection(filter);
                    ObjectId[] objectIds = null;

                    if (psr.Status == PromptStatus.OK)
                    {
                        SelectionSet selectionSet = psr.Value;
                        objectIds = selectionSet.GetObjectIds();
                        ed.WriteMessage("Numbers of Objects selected: " + objectIds.Length.ToString());
                        //AcAp.ShowAlertDialog("Number of objects selected: " + );
                        foreach (ObjectId objectId in objectIds)
                        {
                            Line line = trans.GetObject(objectId, OpenMode.ForRead, true) as Line;
                            string length = line.Length.ToString();
                            SetUcsBy2Points(line.StartPoint, line.EndPoint);

                            Vector3d vector = line.EndPoint - line.StartPoint;

                            Point3d midpoint = line.StartPoint + vector * 0.5;

                            CreateText(trans, midpoint, length + " mm.", 0.00);

                            ed.CurrentUserCoordinateSystem = Matrix3d.Identity;
                        }
                    }
                    else
                    {
                        AcAp.ShowAlertDialog("Nothing was selected.");
                    }

                    trans.Commit();

                }
            }
            catch (System.Exception ex)
            {
                AcAp.ShowAlertDialog("Error found: " + ex.Message + " Found in: " + ex.TargetSite.ToString());
            }
        }

private void SetUcsBy2Points(Point3d pt1, Point3d pt2)
        {
            Editor ed = doc.Editor;
            Vector3d zAxis = ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis;
            Vector3d xAxis = pt1.GetVectorTo(pt2).GetNormal();
            Vector3d yAxis = zAxis.CrossProduct(xAxis).GetNormal();
            Matrix3d mat = Matrix3d.AlignCoordinateSystem(
            Point3d.Origin,
            Vector3d.XAxis,
            Vector3d.YAxis,
            Vector3d.ZAxis,
            pt1,
            xAxis,
            yAxis,
            zAxis);
            ed.CurrentUserCoordinateSystem = mat;
        }

public void CreateText(Transaction tr, Point3d InsertionPoint, string txt, double ang)
        {
            Database db = doc.Database;

            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
            BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;

            using (DBText mtx = new DBText())
            {
                mtx.TextString = txt;
                mtx.Position = InsertionPoint;
                mtx.Height = 10;
                mtx.Rotation = ang;
                
                btr.AppendEntity(mtx);
                tr.AddNewlyCreatedDBObject(mtx, true);
            }
        }

 

 So far I'm getting this Non Unitary UCS Y Axis; Normalizing Message

 

and the Text do not get aligned

Screenshot 2024.png

 

 

 

 

 

 

 

 

 

 

 

 

I will honestly appreciate any help.

 

Thanks a lot

0 Likes
Reply
Accepted solutions (1)
586 Views
8 Replies
Replies (8)

ActivistInvestor
Advisor
Advisor

The image you show does not clearly show what you're trying to achieve.

 

For example, is the text left-justified with its insertion point at the midpoint of the line?

Or, is the text center-justified, etc.

 

An image showing what you expect might be helpful.

 

Aside from that, the current UCS has no role in the operation you want to do. Depending on if the lines all lie in the XY plane, or have endpoints with differing elevations/z-ordinates, the operation only requires you to compute a transformation from the WCS origin to the ECS of each line using the midpoint as the origin, the endpoint as the x-axis, and the normal as the Z-axis. The current UCS has nothing to do with that and you shouldn't need to change it unless if you're using the Command() method to draw the text, which isn't the case here.

 

Once you come up with a transformation matrix for each line, you create each text object at the WCS origin, and then transform it by the matrix and yer done.

0 Likes

_gile
Mentor
Mentor
Accepted solution

Hi,

 

It is necessary to define the plane on which each text is. The direction of the line defines the X axis of this plane, but there can be an infinite number of planes around this axis. It is therefore necessary to also define the Y axis of this plane.

 

Here is an example that uses an arbitrary axis algorithm (inspired by the algorithm used for Object Coordinate Systems) to define this Y axis.

 

[CommandMethod("TEST")]
public void Test()
{
    var doc = AcAp.DocumentManager.MdiActiveDocument;
    var db = doc.Database;
    var ed = doc.Editor;

    var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE"), new TypedValue(8, "SPR") });
    var psr = ed.GetSelection(filter);
    if (psr.Status != PromptStatus.OK) return;

    using (var tr = db.TransactionManager.StartTransaction())
    {
        var currentSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
        foreach (var id in psr.Value.GetObjectIds())
        {
            var line = (Line)tr.GetObject(id, OpenMode.ForRead);
            var vector = line.StartPoint.GetVectorTo(line.EndPoint);
            var origin = line.StartPoint + vector * 0.5;
            var xAxis = vector.GetNormal();

            // Arbitrary algorithm to define the Y axis
            Vector3d yAxis =
                (Math.Abs(xAxis.X) < 0.015625 && Math.Abs(xAxis.Y) < 0.015625) ?
                xAxis.CrossProduct(Vector3d.YAxis).GetNormal() :
                Vector3d.ZAxis.CrossProduct(xAxis).GetNormal();

            var zAxis = xAxis.CrossProduct(yAxis).GetNormal();

            var transform = Matrix3d.AlignCoordinateSystem(
                Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
                origin, xAxis, yAxis, zAxis);

            var text = new DBText
            {
                TextString = $"{line.Length:0.00} mm",
                Justify = AttachmentPoint.BottomCenter,
                AlignmentPoint = Point3d.Origin
            };
            text.TransformBy(transform);
            currentSpace.AppendEntity(text);
            tr.AddNewlyCreatedDBObject(text, true);
        }
        tr.Commit();
    }
}

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

emmanuel_paniaguaEJTWE
Participant
Participant
That works perfect, i still have to go through the algorithm. I don't understand the 0.015625 business, to be honest 😄

I'm more than glad to get an answer from you Gilles, i visited your website for UI inspiration many times.
Thanks again!
0 Likes

ActivistInvestor
Advisor
Advisor

@emmanuel_paniaguaEJTWE wrote:
That works perfect, i still have to go through the algorithm. I don't understand the 0.015625 business, to be honest 😄

The test against 0.015625 is a safeguard to ensure that when the X-Axis is very close to the Z-axis, the code uses a stable method to compute an orthogonal vector (Y-Axis) by switching to a cross product with the Y-axis. This prevents issues that could arise from the cross product of two nearly parallel vectors (which would result in a very small or zero vector).

 

However, you really don't need the arbitrary axis algorithm to solve this problem, Lines have a normal (extrusion direction), which means that you already have two axes:

 

public static void TransformTo(this DBText dbText, Line line)
{
   var xAxis = (line.EndPoint - line.StartPoint).GetNormal();
   var mat = Matrix3d.AlignCoordinateSystem(
       Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
       line.StartPoint, xAxis, line.Normal.CrossProduct(xAxis), line.Normal);
   dbText.TransformBy(mat);
}

 

 

0 Likes

_gile
Mentor
Mentor

@ActivistInvestor  a écrit
However, you really don't need the arbitrary axis algorithm to solve this problem, Lines have a normal (extrusion direction), which means that you already have two

The Normal property of a line is not reliable and can be not perpendicular to the line. In fact, the Normal property of a line indicates the Z axis of the current UCS at the time the line was drawn (the thickness direction).

If you draw a line in WCS from (0, 0, 0) to (20, 15, 10) its Normal property will be (0, 0, 1).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

ActivistInvestor
Advisor
Advisor

Hi @_gile. Wow, I completely forgot about that. Doing a quick search of my old code base on the term CrossProduct, turned up the following, and it is indeed what's used by other code in lieu of the Line's Normal property:

 

public static Vector3d GetNormal(this Line line)
{
   Vector3d dir = (line.EndPoint - line.StartPoint).GetNormal();
   return dir.CrossProduct(
      new Vector3d(dir.Y - dir.Z, dir.Z - dir.X, dir.X - dir.Y)).GetNormal();
}

 

0 Likes

ActivistInvestor
Advisor
Advisor

See the above replies. @_gile is correct that the AAA must be used when dealing with a single Line entity.

 

However, if you're dealing with multiple lines, some of which may be coplanar, and you would like the text associated with each line to be in the same plane the coplanar lines are in, the solution becomes a bit more complicated, and doesn't involve using the AAA.

 

 

0 Likes

emmanuel_paniaguaEJTWE
Participant
Participant
Thanks a lot for all the insights you have shared. It helps a lot
0 Likes