Code Red > .NET

Jigs and Polar Tracking

(1/1)

cmwade77:
In the code below, when the jig starts, if polar tracking is on, it will try to track from the last point as set in the AutoCAD lastpoint variable, this is not how other AutoCAD commands behave.


--- Code - C#: ---public static class JigUtils{    public static double Atan(double y, double x)    {        if (x > 0)            return Math.Atan(y / x);        else if (x < 0)            return Math.Atan(y / x) + Math.PI;        else // x == 0        {            if (y > 0)                return Math.PI / 2;            else if (y < 0)                return -Math.PI / 2;            else // y == 0, theta is undefined                return 0.0;        }    }     public static double ComputeAngle(Point3d startPoint, Point3d endPoint, Vector3d xdir, Matrix3d ucs)    {        Vector3d v = new Vector3d((endPoint.X - startPoint.X) / 2, (endPoint.Y - startPoint.Y) / 2, (endPoint.Z - startPoint.Z) / 2);        double cos = v.DotProduct(xdir);        double sin = v.DotProduct(Vector3d.ZAxis.TransformBy(ucs).CrossProduct(xdir));        return Atan(sin, cos);    }}  public class BulgePolyJig : EntityJig{    public Stack<Point3d> _pointHistory = new Stack<Point3d>();    public Point3d _tempPoint;    Plane _plane;    bool _isArcSeg = true; // Arc is the default    bool _isUndoing = false;    Matrix3d _ucs;    Polyline FlexLine;     public BulgePolyJig(Matrix3d ucs) : base(new Polyline())    {        _ucs = ucs;        CoordinateSystem3d cs = ucs.CoordinateSystem3d;        Vector3d normal = cs.Zaxis;        _plane = new Plane(Point3d.Origin, normal);         Polyline pline = Entity as Polyline;        pline.SetDatabaseDefaults();        pline.Normal = normal;                     Point3d closest = cs.Origin.Project(_plane, normal);        Vector3d disp = closest - cs.Origin;         pline.Elevation = disp.IsCodirectionalTo(normal) ? -disp.Length : disp.Length;         AddDummyVertex();    }     protected override SamplerStatus Sampler(JigPrompts prompts)    {        JigPromptPointOptions jigOpts = new JigPromptPointOptions();         jigOpts.UserInputControls = UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted | UserInputControls.NoNegativeResponseAccepted | UserInputControls.GovernedByOrthoMode;         _isUndoing = false;         Polyline pline = Entity as Polyline;         if (pline.NumberOfVertices == 1)        {            jigOpts.Message = "\nSpecify start point: ";        }        else if (pline.NumberOfVertices > 1)        {            string msgAndKwds = _isArcSeg ? "\nSpecify endpoint of arc or [Line/Undo]: " : "\nSpecify next point or [Arc/Undo]: ";            string kwds = _isArcSeg ? "Line Undo" : "Arc Undo";            jigOpts.SetMessageAndKeywords(msgAndKwds, kwds);        }        else            return SamplerStatus.Cancel; // Should never happen         PromptPointResult res = prompts.AcquirePoint(jigOpts);         if (res.Status == PromptStatus.Keyword)        {            if (res.StringResult.Equals("ARC", StringComparison.CurrentCultureIgnoreCase))                _isArcSeg = true;            else if (string.Equals(res.StringResult, "LINE", StringComparison.OrdinalIgnoreCase))                _isArcSeg = false;            else if (string.Equals(res.StringResult, "UNDO", StringComparison.OrdinalIgnoreCase))                _isUndoing = true;             return SamplerStatus.OK;        }        else if (res.Status == PromptStatus.OK)        {            if (_tempPoint == res.Value)                return SamplerStatus.NoChange;            else            {                _tempPoint = res.Value;                _pointHistory.Push(_tempPoint); // Store the point                                return SamplerStatus.OK;            }        }         return SamplerStatus.Cancel;    }     protected override bool Update()    {        Polyline pl = Entity as Polyline;        FlexLine = pl;        double width = (double)GlobalVariables.MainWindow.Dim_Width.Value; // Get the desired width        // Check if we're setting the first real point        if (pl.NumberOfVertices == 1 && _tempPoint != Point3d.Origin)        {            pl.SetPointAt(0, _tempPoint.Convert2d(_plane));            pl.SetStartWidthAt(0, width);            pl.SetEndWidthAt(0, width);        }        else if (_isArcSeg && pl.NumberOfVertices >= 2)        {            // Safe to assume there are at least 2 vertices here            Point3d lastVertex = pl.GetPoint3dAt(pl.NumberOfVertices - 2);            Vector3d refDir;             // Determine reference direction for calculating bulge            if (pl.NumberOfVertices < 3)            {                refDir = new Vector3d(1.0, 1.0, 0.0);            }            else            {                double bulge = pl.GetBulgeAt(pl.NumberOfVertices - 3);                if (bulge != 0)                {                    CircularArc3d arcSegment = pl.GetArcSegmentAt(pl.NumberOfVertices - 3);                    Line3d tangent = arcSegment.GetTangent(lastVertex);                    refDir = tangent.Direction.MultiplyBy(-1.0);                }                else                {                    Point3d ptBeforeLast = pl.GetPoint3dAt(pl.NumberOfVertices - 3);                    refDir = lastVertex - ptBeforeLast;                }            }             double angle = JigUtils.ComputeAngle(lastVertex, _tempPoint, refDir, _ucs);            double bulgeValue = Math.Tan(angle * 0.5);            pl.SetBulgeAt(pl.NumberOfVertices - 2, bulgeValue);        }        else if (!_isArcSeg && pl.NumberOfVertices >= 2)        {            // For line segments, just ensure the last bulge is set to 0            if (pl.NumberOfVertices > 1)            {                pl.SetBulgeAt(pl.NumberOfVertices - 2, 0);            }        }         // Always update the last vertex position        pl.SetPointAt(pl.NumberOfVertices - 1, _tempPoint.Convert2d(_plane));         return true;    }      public bool IsUndoing    {        get        {            return _isUndoing;        }    }     public void AddDummyVertex()    {        Polyline pline = Entity as Polyline;        double width = (double)GlobalVariables.MainWindow.Dim_Width.Value; // Assuming this is accessible here         // Add a vertex with the specified width. If this is the first vertex, it doesn't form a segment yet,        // so the width will apply to the segment formed when the next vertex is added.        pline.AddVertexAt(pline.NumberOfVertices, new Point2d(0, 0), 0, width, width);    }      public void RemoveLastVertex()    {        Document doc = Application.DocumentManager.MdiActiveDocument;        Editor ed = doc.Editor;        Polyline pline = Entity as Polyline;         if (pline.NumberOfVertices > 0)        {            // Remove the last vertex            pline.RemoveVertexAt(pline.NumberOfVertices - 1);        }         // After removal, check if there are enough vertices to determine the last point        if (pline.NumberOfVertices >= 2)        {            // Get the endpoint of the new last segment            Point3d newLastEndPoint = pline.GetPoint3dAt(pline.NumberOfVertices - 2);             // Update LASTPOINT system variable            Application.SetSystemVariable("LASTPOINT", newLastEndPoint);        }        else        {            // If no vertices are left, consider how you want to handle LASTPOINT.             // You might leave it as it is or reset it to some default value.            // This example resets it to the origin, but you might choose a different approach.            Application.SetSystemVariable("LASTPOINT", Point3d.Origin);        }         // Adjust arc segment flag based on the last segment's bulge, if applicable.        if (pline.NumberOfVertices >= 2)        {            double blg = pline.GetBulgeAt(pline.NumberOfVertices - 2);            _isArcSeg = (blg != 0);        }    }     private void EnsureFlexLinetypeLoaded(Document doc)    {        Database db = doc.Database;        Editor ed = doc.Editor;         using (Transaction tr = db.TransactionManager.StartTransaction())        {            LinetypeTable ltTable = (LinetypeTable)tr.GetObject(db.LinetypeTableId, OpenMode.ForRead);             // Check if "FLEX" linetype is loaded            if (!ltTable.Has("FLEX"))            {                // Determine the path to the linetype file (.lin)                string assemblyLocation = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);                string linFilePath = System.IO.Path.Combine(assemblyLocation, @"Linetypes\Flex.lin");                 // Load the linetype from the file                try                {                    db.LoadLineTypeFile("FLEX", linFilePath);                    ed.WriteMessage("\nLoaded 'FLEX' linetype from file.");                }                catch (System.Exception ex)                {                    ed.WriteMessage($"\nError loading 'FLEX' linetype: {ex.Message}");                }            }            else            {                ed.WriteMessage("\n'FLEX' linetype is already loaded.");            }             tr.Commit();        }    }    public void MakeFlex()    {        SetPolylineLinetypeToFlex(Application.DocumentManager.MdiActiveDocument, FlexLine);               }    private void SetPolylineLinetypeToFlex(Document doc, Polyline pline)    {        string objLayer;        Database db = doc.Database;        using (Transaction tr = db.TransactionManager.StartTransaction())        {            LinetypeTable ltTable = (LinetypeTable)tr.GetObject(db.LinetypeTableId, OpenMode.ForRead);             // Ensure the "FLEX" linetype is available            EnsureFlexLinetypeLoaded(doc);             // Open the LinetypeTable for read            LinetypeTableRecord ltr = (LinetypeTableRecord)tr.GetObject(ltTable["FLEX"], OpenMode.ForRead);             // Set the polyline's linetype to "FLEX"            pline.LinetypeId = ltr.ObjectId;             using (LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable)            {                Main main = GlobalVariables.MainWindow;                objLayer = main.Layer.Text;                if (lt.Has(objLayer))                {                    pline.Layer = objLayer;                }            }             tr.Commit();        }    }      public void DisposeEntity()    {        this.Entity.Dispose();    }      public void Append()    {        Database db = HostApplicationServices.WorkingDatabase;        using (Transaction tr = db.TransactionManager.StartTransaction())        {            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;            BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;             btr.AppendEntity(this.Entity);            tr.AddNewlyCreatedDBObject(this.Entity, true);            tr.Commit();        }    }}

And this is the relevant part of my code that runs this jig:

--- Code - C#: ---                  Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;                  Database db = doc.Database;                  Editor ed = doc.Editor;                   BulgePolyJig jig = new BulgePolyJig(ed.CurrentUserCoordinateSystem);                   while (true)                  {                      PromptResult res = ed.Drag(jig);                       switch (res.Status)                      {                          case PromptStatus.OK:                              jig.AddDummyVertex();                              break;                           case PromptStatus.Keyword:                              if (jig.IsUndoing)                              {                                  jig.RemoveLastVertex();                                                                                                    }                              break;                           case PromptStatus.None:                              jig.RemoveLastVertex(); // Clean up last dummy vertex                              // This is the point where you know the polyline is complete                              // Set the linetype to "FLEX" here                              jig.MakeFlex();                               jig.Append(); // Append to the database                              return;                           default:                              jig.DisposeEntity(); // Dispose of the entity properly                              return;                      }                  }
I have other jigs that have the same issue, but I am hoping that once I figure out a solution here, I can apply that to my other jigs as well.

JohnK:
Please forgive me because I am not good at all with C# (don't know much if at all really), so this reply is more on the generalities of programming. I just happened to see a portion of your code while passing by.

1. Shouldn't you use a `switch` statement? I briefly looked up the switch statement in C# and I think I understand enough to write some basic code to convey my point.

2. I don't understand how C# deals with function overloading in your Atan function but you should probably name that function differently -i.e. prefix with the namespace or program name.

I did not check your math logic in your `atan` function but just typed enough to convey my point about using a switch statement (fixing [glaring] math logic bombs is up to you).

3. Functions with multiple exit points is a stylistic thing but can sometimes be a pain. You can eliminate the multiple exits with #1 above and improve readablilty.

Here is my very crude attempt at writing some C# code.

--- Code - C#: ---public readonly struct Point {  /* NOTE: grabbed from MS help on switch statement but I understand the concept and I need it for my demo */  public Point(double x, double y) => (X, Y) = (x, y);   public double X { get; }  public double Y { get; }} public static double atan(Point point) => point switch {        { X: > 0 } => Math.Atan(point.Y / point.X),             /* X > 0 */        { X: < 0 } => Math.Atan(point.Y / point.X) + Math.PI,   /* X < 0 */        { X: 0, Y: > 0 } => Math.PI / 2,                        /* x = 0 and y > 0 */        { X: 0, Y: < 0 } => -Math.PI / 2,        _ => 0.0,                                               /* discard pattern */}; ...Point pt = new Point(1, 1);Console.WriteLine(atan(pt));...

cmwade77:
Oh, this is definitely a work in progress, I have my own process of getting there in the end, but valid points. I just don't understand why I am getting the Ortho Issue

Navigation

[0] Message Index

Go to full version