Community
AutoCAD Forum
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Spline: tangency to another line.

26 REPLIES 26
SOLVED
Reply
Message 1 of 27
Anonymous
4033 Views, 26 Replies

Spline: tangency to another line.

Dear Forum,

 

How to identify in a spline the point(s) where it is tangent to another line?

 

I have a spline, and I want to find its points of tangency to the horizontal.

 

Object Snap "Tangent" option seems unhelpful here.

 

Sorry for the naivety. I can't find the way.

 

Michele

Labels (1)
26 REPLIES 26
Message 2 of 27
Anonymous
in reply to: Anonymous

i'm going to say you can't since a spline is a free form line depending on the points you pick. No defined geometry.

Message 3 of 27
Anonymous
in reply to: Anonymous

(reply deleted by the author)

Message 4 of 27
Anonymous
in reply to: Anonymous

So if a point called X belongs to a spline, it makes no sense to think at the tangent of that spline in X?

Message 5 of 27
Anonymous
in reply to: Anonymous

If you have a circle or an arc and you draw a line from the center of that object to any point on that object you will be radial and tangent to a 90 degree angle. I don't think you can find a center on a spline unless you explode it.  Like I stated before it's a free form polyline based on the pick points.  If you adjust a pick point the polyline self adjusts to your picks and not to a center point. I ain't no math major but I've learned not to use splines in my work because of their issues.

Message 6 of 27
leeminardi
in reply to: Anonymous

Splines in AutoCAD are mathematically called non-uniform rational B-spline or NURBS for short.  In general when you create a spline in AutoCAD using the Fit or Control Vertex method the spline  can be considered just a B-spline (leaving out the non-uniform aspect).   The more complicated NURBS spline are produced when you start editing the weights of the CVs. Since the spline is defined mathematically the slope of the spline can be determined at any point along the spline by doing a calculus operation known as a derivative.   The derivative of a curve is the slope of the curve.  The math of a NURBS or B-spline is beyond the scope of a discussion here but if you would like a taste of it check out this link.

 

An easy way to determine the slope of a spline in AutoCAD is to trim the spline a the point you want to examine and then look at the location of the first Control Vertex after the trim.  A line going from the end of the trimmed splined to the next CV defines the slope of the spline at its end.

 

For example: the spline in the picture below was defined with 7 Fit points. 

image.png

Let's say we would like to know the slope of the spline where the red line intersects the spline.  We could trim the spline to the red line  and then  display the Control Vertices which would yield the following:

image.png

A line from CV point A to CV point B defines the slope of the spline at A.

Unfortunately I do not believe there is an OSNAP for CVs.  You can however get their location with the list command.

 

A program could be written to examine a B-spline and find points on the spline that have a specified slope.  AutoLISP has a function that outputs the first derivative which could be used to find the point.

lee.minardi
Message 7 of 27
j.palmeL29YX
in reply to: Anonymous


@Anonymous wrote:

... a point called X belongs to a spline, [draw a] tangent of that spline in X?


 

You can draw a line tangent on a Spline in a given point X as shown in >>this video<<.

- Draw a small circle with its centerpoint at X (here the intersection between a line and the spline)

- Draw a line from the one intersection spline/circle to the other intersection spline/circle.

- move the line from its midpoint to the centerpoint of the circle.

- (if needed increase the length of the line)

 

It is "only" an approximated solution. But you can increase the precision of the solution, the smaller the diameter of the circle. I think in most practical cases it an acceptable solution. (And it is not necessary to break the spline. And you can grab the tangent line using osnaps for further purposes).

 

 

 

If my reply solves your problem, click the "accept as solution" button. This can help others find solutions faster

Jürgen Palme
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature

Message 8 of 27
Kent1Cooper
in reply to: Anonymous


@Anonymous wrote:

.... I have a spline, and I want to find its points of tangency to the horizontal. ....


If it were any other kind of curved object, such as a Circle or Arc or [non-spline-curved] Polyline, its bounding box would provide a horizontal tangent edge at the uppermost or lowermost extents, and INTersection Osnap would give you the point of tangency.  Unfortunately, a Spline's bounding box often extends beyond the actual shape of it, reaching out to include at least some Control Vertices [though it's not as simple as that -- not always all of them, and I don't know what determines it].  Here the dashed blue is the bounding box:

SplineBB.PNG

And if you need a tangency-to-horizontal that is not at the extremes, such as at the bottom of the rightward swoop above, the bounding box can't help you.

 

But a routine could be made to step along and test the first-derivative angle at intervals, and when it gets close to horizontal, reduce the size of the intervals, and back up if it went too far, until it gets to a point where the direction is horizontal, but within some tolerance, not with absolute precision.  You could make the tolerance very tight to get very close.  Is that worth pursuing?

Kent Cooper, AIA
Message 9 of 27
leeminardi
in reply to: Kent1Cooper

@j.palmeL29YX  I like your approach for finding the approximate slope of a spline at a point.  Too bad AutoCAD's parametric features do not include splines for constraints or you could make the solution more automatic. 

 

The following lisp program create points at all the locations on a spline where the slope is approximately zero. I arbitrarily set a tolerance of 0.01 for the vertical component of the slope vector  p determined by vlax-curve-getFirstDeriv.  This could be tweaked but the step size for the B-spline's independent variable tt if too big may miss a solution.

image.png

(defun c:zeroslope (/)
; adds points on a spline where the slope is approxmately 0.
; lrm 5/27/2020  
  (vl-load-com)
  (command "_pdmode" 35)
  (setvar "osmode" 0)
  (setq	ent	    (car (entsel))	; select curve
	curve-Obj   (vlax-ename->vla-object ent)
	startSpline (vlax-curve-getStartParam curve-Obj)
	endSpline   (vlax-curve-getEndParam curve-Obj)
	tt	    0.0
	dt	    (/ (- endSpline startSpline) 1000.0)
  )
  (while (< tt endSpline)
    (setq p (vlax-curve-getFirstDeriv curve-Obj tt))
    (setq y (abs (cadr p)))
    (if	(< y 0.01)
      (progn
	(setq tsolution tt)
	(princ "\nt solution = ")
	(princ tsolution)
	(setq pp (vlax-curve-getPointAtParam curve-obj tsolution))
	(command "_point" pp)
      )
    )
    (setq tt (+ tt dt))
  )					;end while  
  (princ "\Done")
  (princ)
)

 

 

 

lee.minardi
Message 10 of 27
leeminardi
in reply to: leeminardi

Here's an improved version of the program that allows the user to specify an angle to use when examining the slope of a spline. A tolerance of 0.5° is used (cosine(0.99996)).  Since the spline is assessed at 1000 points multiple solution may be found in regions of very gradual slope change.

 

(defun c:splineslope (/ curve-Obj startSpline endSpline tt dt p d ang angr
		       up pp)
; adds points on a spline where the slope is at the angle specified
; by the user (within 0.5°). Multiple points within this tolerance may be found.
; The spline is evaluated at 1000 points for the calcuation.  
; lrm 5/28/2020  
  (vl-load-com)
  (command "_pdmode" 35)
  (setvar "osmode" 0)
  (setq ent (car (entsel "\nSelect a spline: "))) ; select curve
  (if (eq (cdr (assoc 0 (entget ent))) "SPLINE")
    (progn
      (setq curve-Obj	(vlax-ename->vla-object ent)
	    startSpline	(vlax-curve-getStartParam curve-Obj)
	    endSpline	(vlax-curve-getEndParam curve-Obj)
	    tt		0.0
	    dt		(/ (- endSpline startSpline) 1000.0)
      )
      (setq ang (getreal "\nEnter slope angle for spline: "))
      (setq angr (* ang (/ pi 180.0)))
      (setq vslope (list (cos angr) (sin angr) 0.0))
      (while (< tt endSpline)
	(setq p (vlax-curve-getFirstDeriv curve-Obj tt))
	(setq d (distance '(0.0 0.0 0.0) p))
	(setq up (mapcar '/ p (list d d d)))
	(if (> (abs (dot vslope up)) 0.99996) ; cosine  0.5°
	  (progn
	    (setq tsolution tt)
	    (setq pp (vlax-curve-getPointAtParam curve-obj tsolution))
	    (command "_point" pp)
	  )
	)
	(setq tt (+ tt dt))
      )					;end while  
      (princ "\Done")
    )					; end if spline
    (princ "\nSpline not selected.")
  )					; end if  
  (princ)
)
;;; Compute the dot product of 2 vectors a and b
(defun dot (a b / dd)
  (setq dd (mapcar '* a b))
  (setq dd (+ (nth 0 dd) (nth 1 dd) (nth 2 dd)))
)
lee.minardi
Message 11 of 27
SEANT61
in reply to: leeminardi

@leeminardi  , nicely done. 

 

I do believe that an iterative approach has the best applicability overall.  With regard to tangency analysis, that methodology should handle b-splines, Non Uniform, Rational or otherwise.

 

The Computer Science 3621 link you posted previously, though, does present an interesting aspect of Bezier and B-Splines.  Much of the higher math presented on that page can be distilled down to a simple graphical representation.

 

In the attached screencast, I rearrange the control cage of a Degree 3 spline to create a degree 2 spline that is the hodograph (Tangency Map) of the former.  With the help of the attached lisp, the hodograph spline can be selected at points that intersect a desired tangency direction and record the parameter of that point.   That same parameter of the Degree 3 spline will have that tangency.

 

My crude lisp routine only works with splines that qualify as Bezier.  The C# routine can process spline with Control points greater than Degree + 1, though they do need to be Non Rational.

 

 

 

 

 

        [CommandMethod("splMM")]
        static public void SplineMM()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            PromptSelectionOptions pso = new PromptSelectionOptions();
            pso.SelectEverythingInAperture = true;
            pso.SinglePickInSpace = true;
            pso.MessageForRemoval = "\nMust be a planar, degree 3 spline: ";
            pso.MessageForAdding = "\nSelect single spline: ";

            TypedValue[] filter = { new TypedValue(0, "SPLINE"),
                                    new TypedValue(71, 3),
                                    new TypedValue(-4, "&="),
                                    new TypedValue(70, 8)};
            SelectionFilter selFilter = new SelectionFilter(filter);

            PromptSelectionResult res = ed.GetSelection(pso, selFilter);

            if (res.Status == PromptStatus.OK)
            {
                ObjectId[] selSet = res.Value.GetObjectIds();
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    Spline spl = tr.GetObject(selSet[0], OpenMode.ForRead) as Spline;
                    if (spl == null) return;

                    BlockTableRecord currSpace = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    Point2dCollection p2dColl = new Point2dCollection();

                    KnotCollection kc = new KnotCollection();

                    kc.Add(0.0);
                    kc.Add(0.0);
                    kc.Add(0.0);
                    kc.Add(1.0);
                    kc.Add(1.0);
                    kc.Add(1.0);

                    NurbCurve2d nc2d = ProcessSpline2d(spl);
                    //p2dColl.Add(nc2d.StartPoint);
                    //p2dColl.Add(nc2d.EndPoint);
                    NurbCurve2d[] c3ds = PrepCurve(nc2d);
                    int ubnd = c3ds.GetUpperBound(0);
                    for (int i = 0; i <= ubnd; i++)
                    {
                        critPts(c3ds[i], p2dColl, kc);
                    }
                    foreach (Point2d p2d in p2dColl)
                    {
                        Point3d p3d = new Point3d(p2d.X, p2d.Y, 0.0);
                        DBPoint dbpt = new DBPoint(p3d);
                        currSpace.AppendEntity(dbpt);
                        dbpt.SetDatabaseDefaults();
                        tr.AddNewlyCreatedDBObject(dbpt, true);
                    }
                    tr.Commit();
                }
            }

        }

        private static NurbCurve2d[] PrepCurve(NurbCurve2d cSpline)
        {
            NurbCurve2d[] nc2ds;

            int deg = cSpline.Degree;
            int cpCount = cSpline.NumControlPoints;
            int IntrvlCount = cpCount + deg;
            NurbCurve2dData nc2dD = cSpline.DefinitionData;
            KnotCollection kc = nc2dD.Knots;
            DoubleCollection dc = kc.GetDistinctKnots();

            int knotCount = dc.Count;
            nc2ds = new NurbCurve2d[knotCount - 1];

            NurbCurve2d trimmed;
            for (int i = 0; i < knotCount - 1; i++)
            {

                trimmed = cSpline.Clone() as NurbCurve2d;
                trimmed.HardTrimByParams(dc[i], dc[i + 1]);
                nc2ds[i] = trimmed;
            }
            return nc2ds;
        }

        private static NurbCurve2d ProcessSpline2d(Spline spl)
        {
            NurbCurve2d nc2d;
            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();
                nc2d = new NurbCurve2d(nd.Degree, kc, Gen2DPtsNurbData(p3cCP), dcw, nd.Periodic);
            }
            else
            {
                nc2d = new NurbCurve2d(nd.Degree, kc, Gen2DPtsNurbData(p3cCP), nd.Periodic);
            }
            spl.Dispose();
            return nc2d;

        }

        private static Point2dCollection Gen2DPtsNurbData(Point3dCollection FitPts)
        {

            Point2dCollection p2dc = new Point2dCollection();
            foreach (Point3d pt in FitPts)
            {

                p2dc.Add(new Point2d(pt.X, pt.Y));
            }
            return p2dc;
        }

        private static void critPts(NurbCurve2d nc2d, Point2dCollection p2dc, KnotCollection kc)
        {
            NurbCurve2d dervGraph = Hodo(nc2d, kc);
            Line2d horz = new Line2d(new Point2d(0, 0), new Point2d(1, 0));
            Line2d vert = new Line2d(new Point2d(0, 0), new Point2d(0, 1));
            Interval intvl = nc2d.GetInterval();
            Double stParam = intvl.LowerBound;
            Double len = intvl.Length;
            DoubleCollection dc = CritPtsParams(dervGraph, horz);
            foreach (Double param in dc)
            {
                Double adjustedParam = stParam + (param * len);
                Point2d interim = nc2d.EvaluatePoint(adjustedParam);//Debug Step
                p2dc.Add(interim);
            }

            dc = CritPtsParams(dervGraph, vert);
            foreach (Double param in dc)
            {
                Double adjustedParam = stParam + (param * len);
                Point2d interim = nc2d.EvaluatePoint(adjustedParam);//Debug Step
                p2dc.Add(interim);
            }
        }

        private static NurbCurve2d Hodo(NurbCurve2d nc2d, KnotCollection kc)
        {

            Point2dCollection p2dc = new Point2dCollection();
            int count = nc2d.NumControlPoints - 1;
            for (int i = 0; i < count; i++)
            {
                Point2d p1 = nc2d.GetControlPointAt(i);
                Point2d p2 = nc2d.GetControlPointAt(i + 1);
                Vector2d p2d = nc2d.GetControlPointAt(i).GetVectorTo(nc2d.GetControlPointAt(i + 1));
                p2dc.Add(new Point2d(p2d.X, p2d.Y));
            }
            NurbCurve2d graph = new NurbCurve2d(2, kc, p2dc, false);
            return graph;
        }

        private static DoubleCollection CritPtsParams(NurbCurve2d nc2d, Line2d Axis)
        {

            CurveCurveIntersector2d cci2d = new CurveCurveIntersector2d(nc2d, Axis);
            int intPts = cci2d.NumberOfIntersectionPoints;
            DoubleCollection dc = new DoubleCollection(intPts);
            for (int i = 0; i < intPts; i++)
            {
                dc.Add(cci2d.GetIntersectionParameters(i)[0]);
            }
            return dc;
        }

 

 

 

 

 

 

 

 

 

(defun c:PickParam (/)
  (vl-load-com)
  (setq ent (car (entsel "\nSelect Hodograph Curve: ")) 
 curve-Obj   (vlax-ename->vla-object ent)
  )
    (initget 1)
    (setq p (getpoint "\nPick Location at desired Tangency: "))
(setq db (vlax-curve-getParamAtPoint curve-Obj (vlax-curve-getclosestpointto curve-Obj p)))
 (print db)

  (setq ent2 (car (entsel "\nSelect Curve for Analysis: "))
 curve-Obj2   (vlax-ename->vla-object ent2)
 )

 (setq pp (vlax-curve-getPointAtParam curve-Obj2 db))
 (command "_point" pp)    
  (princ)
)

 

 

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

************************************************************
May your cursor always snap to the location intended.
Message 12 of 27
Anonymous
in reply to: leeminardi

Thank you for your tips, and for the explanations.

 

Honestly, I couldn't imagine that a problem of such (apparent) simplicity wouldn't have had a click-and-go solution: I have a spline, and I want to contain it within a rectangle.

 

Tangent.jpg

 

As we said, tangency is a matter of derivative, and if we know the formula of our curve, we also should be able to know the tangent (slope) for every point.

 

Using trim @leeminardi  or little circles @j.palmeL29YX  to get to the tangent of the spline in one point (the slope of the spline in X) is clever, but in fact my aim was rather to know where the spline has a certain slope (zero, horizontal): it's the other side of the problem.

 

Unfortunately, my knowledge is not so deep as yours, so I looked at your talk woth sort of embarassment. Thank you @Kent1Cooper for introduce me to "bound", even though not so good for splines: I didn't know of such a tool, where can I find it in AutoCAD?

 

I'm not familiar with LISP: how can I make use of spline-slope.lsp?

Message 13 of 27
leeminardi
in reply to: Anonymous

Extract the file Spline-slope.lsp from the zipped file in my last post.  In AutoCAD go to the Manage tab and click Load Application and select the file Spline-slope.lsp and click Load.

 

YOu now have a new command to use called splineslope!  Just enter it at the command prompt then select a spline and enter an angle. Points will appear on the spline at the specified slope.   If the spline is fairly flat you may get more than one point.  If so, just use the midpoint between them.

image.png

Note the 2 points were created in the area within the red box.  This is because there's a tolerance on the angle of +/- 0.5°.   A smaller tolerance could have been used but this could keep a solution from being calculated.  The command changes the point style to ensure that the points can be seen.

lee.minardi
Message 14 of 27
Kent1Cooper
in reply to: Anonymous


@Anonymous wrote:

….

.... Thank you @Kent1Cooper for introduce me to "bound", even though not so good for splines: I didn't know of such a tool, where can I find it in AutoCAD?

….


>Here< is an example of the use of the (vla-get-BoundingBox) function.

Kent Cooper, AIA
Message 15 of 27
Anonymous
in reply to: leeminardi

Thank you @leeminardi : it works!

 

I also applied zeroslope to a horizontal line, to see it filled up with lots of points!

 

😄

Message 16 of 27
leeminardi
in reply to: SEANT61

@SEANT61  thank you for the compliment.  

I was not familiar with Hodographs but studied your screencast and I think I understand except that when I went to the 5th degree spline example at the link I posted earlier I could not see how the Hodograph was constructed. 

image.png

 I got one that looks like this:

image.png

The hook at the end does not not accurately show the change in slope as you traverse from 8 to 10.  Any idea what I am doing wrong?

 

Thanks.

 

  

lee.minardi
Message 17 of 27
SEANT61
in reply to: leeminardi

Absolutely without certainty, but I think the illustration on that page is not the hodograph for that Degree 5 curve. Quite frankly, I think the hodograph should look more like what you show.


In case you haven’t seen it. Here is the similar description of the Bezier version:https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html

 

I am probably misinterpreting the phrase:


"If the original clamped knot vector is u0(p+1), up+1, ..., um-p-1, um(p+1), then removing the first and the last knots so that the multiplicity of the first and the last knots become p rather than p+1, we have a new knot sequence of m - 1 knots u0(p), up+1, ..., um-p-1, um(p). Then, it can be shown that Ni+1,p-1(u) evaluated on the original knot sequence is equal to Ni,p-1(u) on the new one. Therefore, on the new knot sequence the derivative of a B-spline curve is the following: . . ."


I don’t think, though, that the info pertains to the hodograph structure so much as the parameterization of the resultant curve.


My B-Spline hodograph experiments (using the same vector technique as Bezier) did show results that were in the ball park, though not the exceptional fidelity I found with the Bezier style curves. That could be due to my misinterpretation, or maybe due to AutoCAD’s ofttimes alternate spline mechanics (at least as compared to Rhino3d). I eventually decided to reconfigure the target B-Spline by converting it to multiple Beziers, and analyzing them in turn (See the  private static NurbCurve2d[] PrepCurve(NurbCurve2d cSpline) in the C# routine I posted earlier). That may not be the purest ‘B-Spline Tangency Analysis’ methodology but the .Net API has the arsenal of tools to get it done efficiently. The NurbCurve2d.HardTrimByParams Method is a perfect fidelity B-Spline to multi-Bezier converter.


************************************************************
May your cursor always snap to the location intended.
Message 18 of 27
SEANT61
in reply to: SEANT61

Having looked at the situation again I see that the depiction on the link is correct.  Laying out my own Bezier decomposition creates the Hodograph like that link predicted.

 

Clearly, my interpretation of the math needs refinement.  This investigation might offer a nice peek under the knickers of Spline mechanics.

 

Correction.png


************************************************************
May your cursor always snap to the location intended.
Message 19 of 27
leeminardi
in reply to: SEANT61

Nice work @SEANT61 

 

One thing I know for sure is I am still learning about non-rational and rational B-splines.  Your understanding of them is much better than mine.  I compared your Hodograph curve created from the piecemeal Bezier to the “wrong assumption” curve and could determine no procedure to go directly from the B-spline to the Hodograph. It is interesting to note that the second and next to the last CV for the curve are located at the midpoint of their corresponding vectors.

image.png

To better understand B-splines I created a custom B-spline function in Excel/VBA. It nicely reproduces any degree non-rational B-splines that are created in AutoCAD. I used the CVs from your AutoCAD file to duplicate the 5th degree B-spline. For graphing purposes I choose a delta u of 0.15.

image.png

Looking at the similarities and differences between the B-spline and B-spline derivative functions

image.png

I thought I could modify my Excel Bspline function to make a Bspline-derivative function by making slight changes to the subscripts and range and to reference an array of Q points rather than the original CVs. Keeping track of subscript nomenclature was a real challenge when I created the Bspline program and would be a challenge for me to create the derivative function. A project I’ll put on the back burner unless you want to give it a go!

lee.minardi
Message 20 of 27
SEANT61
in reply to: leeminardi

A tantalizing peek indeed.  Here is the Hodograph reduced to its most elemental form.  You had already spotted some interesting properties in the previous file - perhaps this will make the spreadsheet more manageable. 

 

I'll give the Excel sheet another look myself in the coming days.  In the mean time, keep the thread posted if you make any breakthroughs. 


************************************************************
May your cursor always snap to the location intended.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Forma Design Contest


AutoCAD Beta