Jump to content

Measure-like Command


wannabe

Recommended Posts

I’m not sure if this issue has been resolved but figured I post the .NET version as it currently stands (I’m treating this as a learning exercise so would continue despite the issue’s resolution status).

 

The attached routine should handle Lines, Poly’s (though not yet Fit or Splined), and Splines.

 

A word of caution: This is still a work in progress. Given .NET’s ability to crash AutoCAD off the face of the desktop, only test this routine on inconsequential files.

 

A compiled version of the code below is attached. After a NETLOAD for "CsRefDistAlongPath.dll", the command is rdap.

 

NameSpaces:

 

using System;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.ApplicationServices;

 

Command:

 

        [CommandMethod("rdap")]
       static public void RefDistAlongPath()
       {
           Database db = HostApplicationServices.WorkingDatabase;
           Editor ed = Acad.DocumentManager.MdiActiveDocument.Editor;
           Tolerance tol = new Tolerance(.01, .01); //subject to change
           using (Transaction trans = db.TransactionManager.StartTransaction())
           {
               PromptEntityOptions peo = new PromptEntityOptions("Select target curve: ");
               peo.SetRejectMessage("\nPlease only select a curve entity!");
               peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Curve), false);
               PromptEntityResult per = ed.GetEntity(peo);
               if (per.Status != PromptStatus.OK) return;
               ObjectId oid = per.ObjectId;
               Vector3d z = new Vector3d(0.0, 0.0, 1.0);
               Plane pl = new Plane(new Point3d(0.0,0.0,0.0), z);
               PromptPointOptions ppo = new PromptPointOptions("\nSelect point at which to start measure: ");
               PromptPointResult ppr = ed.GetPoint(ppo);
               if (ppr.Status != PromptStatus.OK) return;
               
               Curve crv = (Curve)trans.GetObject(oid, OpenMode.ForRead, false);
               Point3d p3dnear = ppr.Value;
               p3dnear.Project(pl, z);

               
               EntityToGeom etg = new EntityToGeom(crv);
               if (etg.ConversionSuccess)
               {
                   Curve3d c3d = etg.CurveGeometry.GetProjectedEntity(pl, z) as Curve3d;

                   PointOnCurve3d poc = c3d.GetClosestPointTo(p3dnear);
                   Double param = c3d.GetParameterOf(poc.Point);


                   PromptStringOptions pso = new PromptStringOptions("\nEnter name of block to create reference: ");
                   PromptResult pr = ed.GetString(pso);
                   String blkName = pr.StringResult;
                   BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                   if (!bt.Has(blkName))
                   {
                       ed.WriteMessage("\nCurrent Database does not contain a block of that name!");
                       return;
                   }
                   BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[blkName], OpenMode.ForRead);
                   PromptDistanceOptions pdo = new PromptDistanceOptions("\nIndicate distance along path: ");
                   pdo.AllowArbitraryInput = false;
                   PromptDoubleResult pdr = ed.GetDistance(pdo);
                   if (pdr.Status != PromptStatus.OK) return;
                   Double dist = pdr.Value;
                   param = c3d.GetParameterAtLength(param, dist, true, tol.EqualPoint);

                   BlockTableRecord currSpace = trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                   PointOnCurve3d poc2 = new PointOnCurve3d(c3d, param);

                   Vector3d v3d;

                   try
                   {

                       v3d = poc2.GetDerivative(1);
                   }
                   catch
                   {
                       ed.WriteMessage("Error while acquiring tangent vector!");
                       return;
                   }

                   Double ang = Math.Atan(v3d.Y / v3d.X);
                   BlockReference insert = new BlockReference(poc2.Point, btr.ObjectId);
                   insert.Rotation = ang;

                   currSpace.AppendEntity(insert);
                   insert.SetDatabaseDefaults();
                   trans.AddNewlyCreatedDBObject(insert, true);
               }
               else
               {
                   ed.WriteMessage("General Modeling Error!");
               }
               trans.Commit();
           } 

       }

RefDistAlongPath.zip

Link to comment
Share on other sites

  • Replies 53
  • Created
  • Last Reply

Top Posters In This Topic

  • wannabe

    17

  • SEANT

    14

  • David Bethel

    6

  • ASMI

    5

Apparently the command and the helper class cannot be placed together without exceeding the 10000 character post limit. :(

 

The .Net's do tend to be rather verbose. :unsure:

 

Certainly compared to Lisp. :glare:

 

:)

 

Helper Class:

 

using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;

namespace CsRefDistAlongPath
{
   class EntityToGeom
   {
       //Fields
       private CompositeCurve3d m_compCurve;
       private Curve3d[] m_crvArray;
       private Point3d[] m_points;
       private int m_polyVCount;
       private Boolean m_success;

       //Constructors
       public EntityToGeom(Curve crv)
       {
           string crvTyp = crv.GetType().ToString();
           switch (crvTyp)
           {
               case "Autodesk.AutoCAD.DatabaseServices.Line":
                   LineSegment3d ls3d = new LineSegment3d(crv.StartPoint, crv.EndPoint);
                   m_crvArray = new Curve3d[1];
                   m_crvArray[0] = ls3d;
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.Ray":
                   Ray3d r3d = new Ray3d(crv.StartPoint, crv.EndPoint);
                   m_crvArray = new Curve3d[1];
                   m_crvArray[0] = r3d;
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.XLine":
                   Line3d l3d = new Line3d(crv.StartPoint, crv.EndPoint);
                   m_crvArray = new Curve3d[1];
                   m_crvArray[0] = l3d;
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.Polyline":
                   ProcessPoly(crv, true);
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.Polyline2d":
                   ProcessPoly(crv, false);
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.Polyline3d":

                   ProcessPoly3d(crv);
                   break;

               case "Autodesk.AutoCAD.DatabaseServices.Spline":
                   ProcessSpline(crv);
                   break;


           }
           try
           {
               m_compCurve = new CompositeCurve3d(m_crvArray);
               m_success = true;
           }
           catch
           {
               m_success = false;
           }
       }

       //Properties
       public Boolean ConversionSuccess
       {
           get
           {
               return m_success;
           }
       }


       public int NumOfPolyVerts
       {
           get
           {
               return m_polyVCount;
           }
       }

       public CompositeCurve3d CurveGeometry
       {
           get
           {
               return m_compCurve;
           }

       }

       //Internal
       private void ProcessPoly(Curve crv, Boolean LightWeight)
       {
           Polyline2d p2d;
           if (LightWeight)
           {
               Polyline p = crv as Polyline;
               p2d = p.ConvertTo(false);
               p.Dispose();
           }
           else
           {
               p2d = crv as Polyline2d;
           }
           if (p2d.PolyType == Poly2dType.SimplePoly)//Work in progess
           {
               Database db = HostApplicationServices.WorkingDatabase;
               using (Transaction trans = db.TransactionManager.StartTransaction())
               {
                   m_polyVCount = Convert.ToInt32(p2d.EndParam);
                   m_points = new Point3d[m_polyVCount + 1];
                   m_crvArray = new Curve3d[m_polyVCount];
                   int i = 0;
                   Double bulge = 0.0;
                   Double param = 0.0;
                   foreach (Vertex2d v2d in p2d)
                   {
                       m_points[i] = p2d.VertexPosition(v2d);
                       if (i > 0)
                       {
                           bulge = Math.Round(bulge, 6);
                           if (bulge != 0.0)
                           {
                               param = Convert.ToDouble(i) - 0.5;
                               Point3d pt3 = p2d.GetPointAtParameter(param);
                               m_crvArray[i - 1] = new CircularArc3d(m_points[i - 1], pt3, m_points[i]);
                           }
                           else
                           {
                               m_crvArray[i - 1] = new LineSegment3d(m_points[i - 1], m_points[i]);
                           }
                       }
                       i++;
                       bulge = v2d.Bulge;
                   }
                   p2d.Dispose();
               }
           }
       }



       private void ProcessPoly3d(Curve crv)
       {
           Polyline3d p3d = crv as Polyline3d;
           Database db = HostApplicationServices.WorkingDatabase;
           using (Transaction trans = db.TransactionManager.StartTransaction())
           {
               PolylineVertex3d pv3d;//New addition
               m_polyVCount = Convert.ToInt32(p3d.EndParam);
               m_points = new Point3d[m_polyVCount + 1];
               m_crvArray = new Curve3d[m_polyVCount];
               int i = 0;
               foreach (ObjectId oid in p3d)
               {
                   pv3d = trans.GetObject(oid, OpenMode.ForRead) as PolylineVertex3d;
                   m_points[i] = pv3d.Position;
                   if (i > 0)
                   {
                       m_crvArray[i - 1] = new LineSegment3d(m_points[i - 1], m_points[i]);
                   }
                   i++;
               }
               p3d.Dispose();
           }
       }

       private void ProcessSpline(Curve crv)
       {
           Spline spl = crv as Spline;
           NurbCurve3d nc3d;
           if (spl.HasFitData)
           {
               Vector3dCollection v3c;
               FitData fd = spl.FitData;
               if (fd.TangentsExist)
               {
                   v3c = TangentVectorContainer(fd);
                   nc3d = new NurbCurve3d(fd.GetFitPoints(), v3c, spl.IsPeriodic);
               }
               else
               {
                   nc3d = new NurbCurve3d(fd.GetFitPoints(), new Vector3d(), new Vector3d(), false, false);   
               }
           }
           else
           {
               NurbsData nd = spl.NurbsData;
               DoubleCollection dc = nd.GetKnots();
               KnotCollection kc = new KnotCollection();
               foreach (Double d in dc)
               {
                   kc.Add(d);
               }
               Point3dCollection p3cCP = nd.GetControlPoints();
               if (nd.Rational)
               {
                   DoubleCollection dcw = nd.GetWeights();
                   nc3d = new NurbCurve3d(nd.Degree, kc, p3cCP, dcw, nd.Periodic);
               }
               else
               {
                   nc3d = new NurbCurve3d(nd.Degree, kc, p3cCP, nd.Periodic);
               }
           }
           m_crvArray = new Curve3d[1];
           m_crvArray[0] = nc3d;
           spl.Dispose();
       }

       private Vector3dCollection TangentVectorContainer(FitData fd)
       {
           Vector3dCollection v3c = new Vector3dCollection(2);
           v3c.Add(fd.StartTangent);
           v3c.Add(fd.EndTangent);
           return v3c;
       }
   }

}

Link to comment
Share on other sites

WOW! If that's what .NET looks like, I see why I'll pass on it. Congrats just on being able type that without errors. -David

Link to comment
Share on other sites

. . . . Congrats just on being able type that without errors. -David

 

Believe me when I say there were plenty of errors typed along the way that needed correcting.

 

 

But, to be honest, this routine could have been considerably more concise. The “Helper Class” is the start of a very generic processor to convert AutoCAD entities into there pure geometrical counterparts. This will allow for geometries that could not be expressed otherwise.

 

For instance, if a Lightweight Polyline, with several “bulges”, was inclined off the WCS and a routine had to process distance along the entire span, but also ignore all “Z” elevations, the pertinent curve has straight lines and elliptical arcs. That scenario is something that the “Managed” CompositeCurve3d can process.

 

It is likely possible with lisp, but not without considerable code.

Link to comment
Share on other sites

Believe me when I say there were plenty of errors typed along the way that needed correcting.

 

 

But, to be honest, this routine could have been considerably more concise. The “Helper Class” is the start of a very generic processor to convert AutoCAD entities into there pure geometrical counterparts. This will allow for geometries that could not be expressed otherwise.

 

For instance, if a Lightweight Polyline, with several “bulges”, was inclined off the WCS and a routine had to process distance along the entire span, but also ignore all “Z” elevations, the pertinent curve has straight lines and elliptical arcs. That scenario is something that the “Managed” CompositeCurve3d can process.

 

It is likely possible with lisp, but not without considerable code.

 

SEANT, you are a genius.

Link to comment
Share on other sites

Hi Seant,

 

How does your command assert that the selected point (ppo) is actually located on the polyline?

 

Cheers for all your input.

Link to comment
Share on other sites

Believe me when I say there were plenty of errors typed along the way that needed correcting.

 

 

But, to be honest, this routine could have been considerably more concise. The “Helper Class” is the start of a very generic processor to convert AutoCAD entities into there pure geometrical counterparts. This will allow for geometries that could not be expressed otherwise.

 

For instance, if a Lightweight Polyline, with several “bulges”, was inclined off the WCS and a routine had to process distance along the entire span, but also ignore all “Z” elevations, the pertinent curve has straight lines and elliptical arcs. That scenario is something that the “Managed” CompositeCurve3d can process.

 

It is likely possible with lisp, but not without considerable code.

 

Sean, you're ahead of me in the ACAD .NET stakes, but I'd like to know why dont you dont think using the distance between the vertices of each GS marker would work?

 

Excuse me if I'm on another planet and have miscontrued your intentions.

 

As per my above post, your time and effort is appreciated.

Link to comment
Share on other sites

Hi Seant,

 

How does your command assert that the selected point (ppo) is actually located on the polyline?

 

Cheers for all your input.

 

You do bring up a good point. The routine actually processes a point on the “Projected” curve nearest to the projected point picked by the operator.

 

I envision the operator making the selection via some OSNAP, to ensure a predictable start location, but should include some conditional for a reasonable proximity.

 

 

PromptPointResult ppr = ed.GetPoint(ppo);
               if (ppr.Status != PromptStatus.OK) return;
               
               Curve crv = (Curve)trans.GetObject(oid, OpenMode.ForRead, false);
               Point3d p3dnear = ppr.Value;
               p3dnear.Project(pl, z);

               
               EntityToGeom etg = new EntityToGeom(crv);
               if (etg.ConversionSuccess)
               {
                   Curve3d c3d = etg.CurveGeometry.GetProjectedEntity(pl, z) as Curve3d;

                   PointOnCurve3d poc = c3d.GetClosestPointTo(p3dnear);
                   Double param = c3d.GetParameterOf(poc.Point);

Link to comment
Share on other sites

Sean, you're ahead of me in the ACAD .NET stakes, but I'd like to know why dont you dont think using the distance between the vertices of each GS marker would work?

 

Excuse me if I'm on another planet and have miscontrued your intentions.

 

As per my above post, your time and effort is appreciated.

 

 

For a straight segment 2dPolyline, that method would have worked fine. To keep the EntityToGeom class (the aforementioned “Helper Class”) as generic as possible, though, I included entities that I don’t believe would be factors in your situation.

 

While I imagine roads and tracks would have both straight and arc sections, they probably would not be constrained to one particular plane in reality. 3dPolys and 3d splines seem like the best tools for an accurate model.

 

Other industries and application, however, may be more likely to find use for processing 2dPolylines, arcs and all, in such a manner.

Link to comment
Share on other sites

Excellent example SEANT! Thanks, I have found pair of useful things for myself because now too I study C #. .NET a powerful tool, only one is bad - it is necessary large volume of a code for the decision of the elementary tasks :x On LISP I do the same for 15 minutes.

 

(defun c:rdap2(/ oldOsn cCur fPt pDis cBlk cDis nPt fDer rAng)
 (vl-load-com)
 (setq oldOsn(getvar "OSMODE"))
 (setvar "OSMODE" 515)
 (if(and(setq cCur(entsel "\nSelect tangent curve: "))
      (vlax-curve-GetArea(setq cCur(car cCur))))
   (if(and(setq fPt(getpoint "\nSelect point at which to start measure: "))
 (setq pDis(vlax-curve-GetDistAtPoint cCur fPt)))
     (if(and(setq cBlk(getstring T "\nEnter name of block to create reference: "))
     (tblsearch "BLOCK" cBlk))
(if(setq cDis(getdist "\nIndicate distance along path: "))
  (if(setq nPt(vlax-curve-GetPointAtDist cCur(+ pDis cDis)))
    (progn
      (setq fDer(vlax-curve-getFirstDeriv cCur
                          (vlax-curve-getParamAtPoint cCur nPt))
	    rAng(angtos(-(/ pi 2)(atan(/(car fDer)(cadr fDer))))))
      (setvar "CMDECHO" 0)
      (command "_.-insert" cBlk "_s" "1" "_r" rAng nPt)
      (setvar "CMDECHO" 1)
      ); end progn
    ); end if
  ); end if
(princ "\n<!> Current Database does not contain a block of that name <!>")
); end if
     ); end if
   ); end if
 (setvar "OSMODE" oldOsn)
 (princ)
 ); end of c:rdap2

 

To tell the truth, it directs me at reasonings... I think, it cannot is necessary for me? But all the same I study C #...

Link to comment
Share on other sites

Asmi, I would not to go head to head with you or any of the other Lisp programmers in a challenge of the most terse, economical code. You guys do get a lot done with little typing.

 

The return for all that typing with .NET is the broader scope, and finer control offered by the closer relation to AutoCAD’s primary code base. There seems to be a lot in there that Autodesk has not made available to either Lisp or VBA, or even the user via the drawing editor. :huh:

 

Some of that available functionality was used to process the Point – Measure – Point requirement on the inclined poly, but the measure only applied to the projected geometry. Granted, this may be a solution to an, as of yet, non-existent problem (my favorite type of project, actually, very little stress).

 

Managed ObjectARX . . . There’s no telling what else we may find in there. :)

Poly_Projection.dwg

Link to comment
Share on other sites

Actually it is pleasant to me .NET. But I have started to be afraid for AutoLISP, especially for its object oriented part. If Autodesk will bury VBA, and together with COM interface that most likely will not develop objective programming on AutoLISP. For a wide range of users .NET is not alternative LISP because in .NET there is no place for likers. And you should solve who you are - the AutoCAD user or the AutoCAD programmer.

 

And still LISP beautiful and logical language having huge set of algorithms in comparison with other languages. I hope that transition to multicore processors will inhale in it a new life, after all it is simply created for the distributed calculations. I speak about LISP in a broad sense instead of about AutoLISP.

Link to comment
Share on other sites

Despite my familiarity with VBA, I do believe the Lisp and .NET/ObjectARX would provide the better combination if Autodesk planned a sensible spread between immediate and structured programming. I can’t always say, however, that Autodesk does what I think is sensible.

 

I do know that no other language enjoys the popularity of AutoLisp. It is why I suspect it will go on indefinitely, even if it has to be maintained and enhanced through some third party (perhaps even Open Source).

 

With regard to multicores and “Functional Programming”, it does sound like that is the next big evolutionary step. We have to give a lot of credit to the creators of LISP; that foundation in logic has gotten the most out of computers through 5 decades (and counting) of hardware development.

Link to comment
Share on other sites

My take is that as long as Autodesk can charge an extra $3K USD for full blown ACAD vs LT just because it is AutoLISP capable, AutoLISP will not go away. -David

Link to comment
Share on other sites

With AutoLISP let's hope for the best :roll: But for now at me on a wall hangs A1 format with the diagramme of Managed OARX Classes. Their quantity differs from other Unmanaged Classes a little :wink: But and it on couple of years will suffice me for studying 8)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...