Author Topic: How to detect a line near a Point? And then get the distance between them?  (Read 11680 times)

0 Members and 1 Guest are viewing this topic.

waterharbin

  • Guest
Hello,everyone? Is it possible to "detect" a line near a point? Let's suppose it is the nearest line to be detected.If yes, then return the distance.
If a point is not enough,what about a line? "Detect" the nearest parallel line,and then return the distance?
Any suggestion or code is helpful.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #1 on: February 10, 2012, 03:42:03 AM »

I've done this using a multisided polygon as a fence for a selection set.
loop through, minutely increasing the size of the polygon untill a suitable object gets caught in the selection.

Perhaps not really elegant but seemed to work fine.

Regards

kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

waterharbin

  • Guest
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #2 on: February 10, 2012, 07:37:25 AM »
Hell0,Kerry.Thanks.Can you give me some codes for me to start with?I am totally known nothing about this at the present.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #3 on: February 10, 2012, 06:20:44 PM »

I'll be away for a few hours ... will have a look at a sample when I come back, unless someone else pops up a solution in the meantime.

Something to think about :
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #4 on: February 11, 2012, 01:27:06 PM »
Hi,

Here's another way, certainly slower if there're many entities in the current space, but much more easy to code. :evil:

Code - C#: [Select]
  1. using System.Linq;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Runtime;
  7.  
  8. namespace ClosestLineSample
  9. {
  10.     public class Commands
  11.     {
  12.         [CommandMethod("Test")]
  13.         public void Test()
  14.         {
  15.             Document doc = Application.DocumentManager.MdiActiveDocument;
  16.             Database db = doc.Database;
  17.             Editor ed = doc.Editor;
  18.             PromptPointResult ppr = ed.GetPoint("\nSpecify a point: ");
  19.             if (ppr.Status != PromptStatus.OK) return;
  20.             Point3d pt = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
  21.             using (Transaction tr = db.TransactionManager.StartTransaction())
  22.             {
  23.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  24.                 var closest = btr
  25.                     .Cast<ObjectId>()
  26.                     .Select(id => tr.GetObject(id, OpenMode.ForRead) as Line)
  27.                     .Where(ent => ent != null)
  28.                     .Select(line => new { Line = line, Dist = pt.DistanceTo(line.GetClosestPointTo(pt, false)) })
  29.                     .Aggregate((l1, l2) => l1.Dist < l2.Dist ? l1 : l2);
  30.                 if (closest != null)
  31.                 {
  32.                     closest.Line.Highlight();
  33.                     ed.WriteMessage("\nDistance = {0}", closest.Dist);
  34.                 }
  35.                 tr.Commit();
  36.             }
  37.         }
  38.     }
  39. }
  40.  
Speaking English as a French Frog

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #5 on: February 11, 2012, 06:00:28 PM »

Looks like a fun solution gile.

Querying the complete database may take a little longer, yes, but thankfully doesn't have to deal with the requirement that selections must be in the current view.

The linq makes for a nice solution.

I haven't ananlysed the code, but the distance will be in the WCS, yes ? as will the Point parameter.

Regards,

kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Gasty

  • Newt
  • Posts: 90
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #6 on: February 11, 2012, 09:27:51 PM »
Hi,

The distance is invariant under CS changes if all the points are in the same CS.

Gaston Nunez

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #7 on: February 12, 2012, 03:58:47 AM »
Thanks Kerry.
Right Gaston.

In the example, the point got with Editor.GetPoint() is defined in current UCS and have to be transformed from current UCS to WCS to get the distance to the point returned by Line.GetClosestPointTo() which is always WCS defined.

Linq allows to write code in a more 'LISPy' way.
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #8 on: February 12, 2012, 04:30:44 AM »
Maybe some aren't very comfortable with the Linq functional style, so, here's a more traditional impertive implementation.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Generic;
  7.  
  8. namespace ClosestLineSample
  9. {
  10.     public class Commands
  11.     {
  12.         [CommandMethod("Test")]
  13.         public void Test()
  14.         {
  15.             Document doc = Application.DocumentManager.MdiActiveDocument;
  16.             Database db = doc.Database;
  17.             Editor ed = doc.Editor;
  18.             PromptPointResult ppr = ed.GetPoint("\nSpecify a point: ");
  19.             if (ppr.Status != PromptStatus.OK) return;
  20.             Point3d pt = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
  21.             using (Transaction tr = db.TransactionManager.StartTransaction())
  22.             {
  23.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  24.                 Line closest = null;
  25.                 double shortest = 0.0;
  26.                 foreach (ObjectId id in btr)
  27.                 {
  28.                     Line line = tr.GetObject(id, OpenMode.ForRead) as Line;
  29.                     if (line != null)
  30.                     {
  31.                         double dist = pt.DistanceTo(line.GetClosestPointTo(pt, false));
  32.                         if (closest == null)
  33.                         {
  34.                             closest = line;
  35.                             shortest = dist;
  36.                         }
  37.                         else if (shortest > dist)
  38.                         {
  39.                             closest = line;
  40.                             shortest = dist;
  41.                         }
  42.                     }
  43.                 }
  44.                 if (closest != null)
  45.                 {
  46.                     closest.Highlight();
  47.                     ed.WriteMessage("\nDistance = {0}", shortest);
  48.                 }
  49.                 tr.Commit();
  50.             }
  51.         }
  52.     }
  53. }
  54.  
« Last Edit: February 12, 2012, 06:25:47 AM by gile »
Speaking English as a French Frog

waterharbin

  • Guest
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #9 on: February 12, 2012, 06:52:44 AM »
Hi,gile.If I want to rule out the case which distance is 0(The point lays in the straight line).I should change your great code liks this?
Code: [Select]
foreach (ObjectId id in btr)
                {
                    Line line = tr.GetObject(id, OpenMode.ForRead) as Line;
                    if (line != null)
                    {
                        double dist = pt.DistanceTo(line.GetClosestPointTo(pt, false));

                      [b]if (dist == 0)
                            continue;[/b]                     
                     if (closest == null)
                        {
                            closest = line;
                            shortest = dist;
                        }
                        else if (shortest > dist)
                        {
                            closest = line;
                            shortest = dist;
                        }
                    }
                }

By the way.The codes in your If...else if ... are the same.Why this?
« Last Edit: February 12, 2012, 06:59:19 AM by waterharbin »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #10 on: February 12, 2012, 07:15:57 AM »
Quote
By the way.The codes in your If...else if ... are the same.Why this?
You're right.
The first if will only occur for the first iteration, the else if for the following ones.

It might have bee written:
(if (closest == null || shortest > dist) { ... }

Or, introducing your rule:
(if (closest == null || (dist != 0.0 && shortest > dist)) { ... }
Speaking English as a French Frog

waterharbin

  • Guest
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #11 on: February 12, 2012, 09:31:55 AM »
Thanks to gile and Kerry.Job is done! This is the codes.Using a SelectCrossingWindow to reduse the number of entitier.
Code: [Select]
public static void UseCrossingWindow()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                PromptPointResult ppr = ed.GetPoint("\n Sepecify the first Point:");

                if (ppr.Status != PromptStatus.OK) return;

                Point3d pt1 = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
               
                //Get another point to form a SeltectCrossingWindow
                ppr = ed.GetPoint("\n Sepecify the second Point:");

                if (ppr.Status != PromptStatus.OK) return;

                Point3d pt2 = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
               
                // Create a crossing window based on the two selected points
                PromptSelectionResult acSSPrompt;
               
                acSSPrompt = ed.SelectCrossingWindow(pt1, pt2);
               
                // If the prompt status is not OK, do nothing.
                if (acSSPrompt.Status != PromptStatus.OK)
                    return;

                SelectionSet acSSet = acSSPrompt.Value;

                ObjectId[] ids = acSSet.GetObjectIds();


                Line closest = null;
                double shortest = 0.0;

                foreach (ObjectId id in ids)
                {
                    Line line = trans.GetObject(id, OpenMode.ForRead) as Line;
                    if (line != null)
                    {                       
                        double dist = pt1.DistanceTo(line.GetClosestPointTo(pt1, false));

                        if (dist == 0)
                            continue;

                        if (closest == null)
                        {
                            closest = line;
                            shortest = dist;
                        }
                        else if (shortest > dist)
                        {
                            closest = line;
                            shortest = dist;
                        }
                    }
                }

                if (closest != null)
                {
                    closest.Highlight();
                    ed.WriteMessage("\nDistance = {0}", shortest);
                }


                /*
                double shortest = 0.0;
                foreach (ObjectId id in ids)
                {
                    Entity ent = (Entity)trans.GetObject(id, OpenMode.ForWrite);

                    if (ent.GetType().Name == "Line")
                    {
                        Line myLine = (Line)trans.GetObject(id, OpenMode.ForWrite);

                        double dist = pt1.DistanceTo(myLine.GetClosestPointTo(pt1,false));

                        if (dist!=0 && shortest > dist)
                        {
                            shortest = dist;
                        }

                    }
                }     //End of foreach loop
                ed.WriteMessage("\nDistance = {0}", shortest);
                */
                               
                trans.Commit();
            }     
        }
By the way.The codes which have be commented out don't work. The result is "0".
And another question,how to rule out other kinds of entities when define the SelectCrossingWindow?


Hoping it will be helpful someone else!

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #12 on: February 12, 2012, 11:53:41 AM »
1_ The Editor selection methods require current UCS defined points, so you do not have to use TransformBy(ed.CurrentUserCoordinateSystem); on the points returned by Editor.GetPoint().

2_ The commented code you posted do not work because the 'shortest' variable is assign to 0.0 and will never change because it will always be lower than dist. Look at the code I gave, in the first iteration 'dist' value is assigned to 'shortest'.

3_ You can use SelectionFilter to select only Line objects. One constructor of the SelectionFilter  type take a TypedValue array as parameter.
Code - C#: [Select]
  1. TypedValue[] filter = new TypedValue[1]{ new TypedValue(0, "LINE") };
  2. acSSPrompt = ed.SelectCrossingWindow(pt1, pt2, new SelectionFilter(filter));
Have a look at the AutoCAD .NET Developper's Guide > Create and Edit AutoCAD Entities > Work with Selection Sets > Define Rules for Selection Filters (and the sub topics).
Speaking English as a French Frog

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
This is a variation on Giles' post above that is looking for coincident objects with a tolerance in the Y direction.

Code - C#: [Select]
  1. /// <summary> how far apart the X positions can be before declaring them totally related. </summary>
  2. double XoffsetCloseEnough = 5;
  3.  
  4. /// <summary> how far apart the Y positions can be before declaring them unrelated. </summary>
  5. double YoffsetTooFarAway  = 1;
  6.  
  7. /// <summary> Random number that just happens to be 500. </summary>
  8. double shortest = 500;
  9.  
  10. /// <summary> This is the same Type as a thing in things </summary>
  11. TypeOfThing closestThing = null;
  12.  
  13. foreach (TypeOfThing thing in things)
  14. {
  15.   if (Math.Abs(thing.Position.Y - otherThing.Position.Y) > YoffsetTooFarAway) continue;
  16.  
  17.   double distance = Math.Abs(thing.Position.X - otherThing.Position.X);
  18.  
  19.   if (distance < shortest)
  20.   {
  21.     closestThing = thing;
  22.     shortest = distance;
  23.     if (distance < XoffsetCloseEnough) break; // That'll do, Pig, that'll do.
  24.   }
  25. }
  26.  

Notice I omitted the null check in the loop because there is a null check after the loop anyway. Null checks are cheap anyway so it won't kill you.

I also added a break; to bail out of the loop when it gets close enough,  just a premature optimi[zs]ation (hello Regex fans) based on where I expect these objects to be. In my case the objects were AttributeReferences.

BKSpurgeon

  • Mosquito
  • Posts: 6
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #14 on: December 21, 2016, 06:20:28 AM »
Mr Gilles thank you this code is very nice.  As I understand it, if applied with the GetObjects then you can string things together with to get whittle down to a super filtered set of entities?

e.g.

Code: [Select]
using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                IEnumerable<Entities> filteredEntities = objectIDs.GetObjects<Entity>(OpenMode.ForRead, false, true).Where( predicate).Select(predicate) etc. etc. ;
            }


Where GetObjects is:

Code: [Select]

        public static IEnumerable<T> GetObjects<T>(
            this IEnumerable<ObjectId> ids,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false,
            bool forceOpenOnLockedLayers = false) where T : DBObject
        {
            if (ids.Any())
            {
                TransactionManager tm = ids.First().Database.TransactionManager;

                RXClass rxclass = RXClass.GetClass(typeof(T));
                if (typeof(T) == typeof(DBObject))
                {
                    foreach (ObjectId id in ids)
                    {
                        yield return (T)tm.GetObject(id, mode, openErased,
                           forceOpenOnLockedLayers);
                    }
                }

                else if (rxclass.GetHasChildren())
                {
                    foreach (ObjectId id in ids)
                    {
                        if (id.ObjectClass.IsDerivedFrom(rxclass))
                            yield return (T)tm.GetObject(id, mode, openErased,
                               forceOpenOnLockedLayers);
                    }
                }
                else
                {
                    foreach (ObjectId id in ids)
                    {
                        if (id.ObjectClass == rxclass)
                            yield return (T)tm.GetObject(id, mode, openErased,
                               forceOpenOnLockedLayers);
                    }
                }
            }
        }

        // Returns true if at least one RXClass is derived from
        // the given RXClass:

        public static bool GetHasChildren(this RXClass rxclass)
        {
            foreach (DictionaryEntry e in SystemObjects.ClassDictionary)
            {
                if (rxclass == ((RXClass)e.Value).MyParent)
                    return true;
            }
            return false;
        }





Hi,

Here's another way, certainly slower if there're many entities in the current space, but much more easy to code. :evil:

Code - C#: [Select]
  1. using System.Linq;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Runtime;
  7.  
  8. namespace ClosestLineSample
  9. {
  10.     public class Commands
  11.     {
  12.         [CommandMethod("Test")]
  13.         public void Test()
  14.         {
  15.             Document doc = Application.DocumentManager.MdiActiveDocument;
  16.             Database db = doc.Database;
  17.             Editor ed = doc.Editor;
  18.             PromptPointResult ppr = ed.GetPoint("\nSpecify a point: ");
  19.             if (ppr.Status != PromptStatus.OK) return;
  20.             Point3d pt = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
  21.             using (Transaction tr = db.TransactionManager.StartTransaction())
  22.             {
  23.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  24.                 var closest = btr
  25.                     .Cast<ObjectId>()
  26.                     .Select(id => tr.GetObject(id, OpenMode.ForRead) as Line)
  27.                     .Where(ent => ent != null)
  28.                     .Select(line => new { Line = line, Dist = pt.DistanceTo(line.GetClosestPointTo(pt, false)) })
  29.                     .Aggregate((l1, l2) => l1.Dist < l2.Dist ? l1 : l2);
  30.                 if (closest != null)
  31.                 {
  32.                     closest.Line.Highlight();
  33.                     ed.WriteMessage("\nDistance = {0}", closest.Dist);
  34.                 }
  35.                 tr.Commit();
  36.             }
  37.         }
  38.     }
  39. }
  40.  

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How to detect a line near a Point? And then get the distance between them?
« Reply #15 on: December 21, 2016, 07:06:56 AM »
Hi,

keep in mind GetObjects<T> already filters by Entity type (T).

Code - C#: [Select]
  1. var closest = btr
  2.     .GetObjects<Line>(OpenMode.ForRead, false, true)
  3.     .Select(line => new { Line = line, Dist = pt.DistanceTo(line.GetClosestPointTo(pt, false)) })
  4.     .Aggregate((l1, l2) => l1.Dist < l2.Dist ? l1 : l2);
« Last Edit: December 21, 2016, 07:12:12 AM by gile »
Speaking English as a French Frog