Author Topic: Find loop by click  (Read 2057 times)

0 Members and 1 Guest are viewing this topic.

dinekro

• Mosquito
• Posts: 3
Find loop by click
« on: January 16, 2013, 04:40:50 pm »
Hi all!
Sory for my English..

I have the following question.
In the drawing, there are many closed loops. Let's say its are closed polylines.
My problem is: When the user clicks on the screen. I must find loop, inside of which was made-clicking.

Write in C #.

The first thing that comes to mind:
1) Implement the routine: test point owned to a closed polyline.
2) Find all the circuits in BoundaryBox which gets the input point.
3) Check for each hit.

Looks somewhat complicated for such a simple, at first glance, the problem.

At first I went the other way. I thought that the method Editor.Snap (snapMode, inputPoint) returns the nearest snap point. Then I would choose the snap mode "nearest", and get a point at the nearest loop (polyline). Most likely this would be the desired loop. But, as far as I understand, this method not suitable...

Maybe there is any native way to implement this simple task? I thought I still use TraceBoundary, but it creates a new object. But I want that the object that the drawing.
Maybe, if we transform the polylines to the blocks - will be easier?

« Last Edit: January 16, 2013, 04:53:58 pm by dinekro »

kaefer

• Swamp Rat
• Posts: 571
Re: Find loop by click
« Reply #1 on: January 17, 2013, 10:29:09 am »
1) Implement the routine: test point owned to a closed polyline.
2) Find all the circuits in BoundaryBox which gets the input point.
3) Check for each hit.

That could work, as long as your "loops" are rectangular *and* perpendicular to your active view. You didn't specify exactly what you are expecting as input.

The fairly new TraceBoundary method might do too, except that it would be very inefficient.

For boundaries composed of straight segments, I've utilized successfully this code from our friends doing Revit.

There are tons of other algorithms out there. For example, there's the ray casting method, which can be used in the general case (Polyline with straight and bulged segments).

dinekro

• Mosquito
• Posts: 3
Re: Find loop by click
« Reply #2 on: January 17, 2013, 01:52:20 pm »

"Loop" is polyline with line and arc segments. Without selfintersections. And, yes, its are perpendicular to my active view. Its are in XOY plane.

Thank You for algorithms! I will try utilizing its..

fixo

• Swamp Rat
• Posts: 856
• My location is not in the Hitleropa
Re: Find loop by click
« Reply #3 on: January 17, 2013, 05:04:00 pm »
FYI,
Code: [Select]
`        [CommandMethod("tpp", CommandFlags.UsePickSet | CommandFlags.Redraw)]        public static void testPickPoint()        {            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;            Database db = doc.Database;            Editor ed = doc.Editor;            string curlay = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("CTAB").ToString();            ObjectIdCollection ids = new ObjectIdCollection();            SortedList<ObjectId, double> distances = new SortedList<ObjectId, double>();            // Let our selection to only select closed polylines            PromptSelectionOptions pso = new PromptSelectionOptions();            pso.MessageForAdding = "\nSelect polygons";            // Add polyline layer for better selection            SelectionFilter filter = new SelectionFilter(new TypedValue[]  {            new TypedValue((int)DxfCode.Start, "LWPOLYLINE"),            new TypedValue(70, 1),            new TypedValue((int)DxfCode.LayoutName, curlay)});            PromptSelectionResult psr = ed.SelectAll(filter);            if (psr.Status != PromptStatus.OK) return;            if (psr.Value.GetObjectIds().Length == 0) return;            try            {                Transaction tr = db.TransactionManager.StartTransaction();                using (tr)                {                    foreach (SelectedObject selobj in psr.Value)                    {                        Polyline pline = tr.GetObject(selobj.ObjectId, OpenMode.ForRead) as Polyline;                        if (pline != null)                        {                            ids.Add(pline.ObjectId);                        }                    }                    if (ids == null) return;                    Point3d pick;                    PromptPointOptions opt = new PromptPointOptions("\nPick a point inside the polygon:");                    PromptPointResult res = ed.GetPoint(opt);                    if (res.Status != PromptStatus.OK) return;                    pick = res.Value;                    Point3d cp;                    foreach (ObjectId id in ids)                    {                        DBObject obj = tr.GetObject(id, OpenMode.ForRead);                        if (obj == null) return;                        Polyline poly = obj as Polyline;                        if (poly == null) return;                        {                            cp = poly.GetClosestPointTo(pick, false);                            double dist = pick.DistanceTo(cp);                            if (distances.ContainsKey(poly.ObjectId))                            {                                double dd = distances[poly.ObjectId];                                if (dist < dd)                                {                                    distances[poly.ObjectId] = dist;                                }                            }                            else                            {                                distances.Add(poly.ObjectId, dist);                            }                        }                    }                    // Retrieve minimal distance with relatively linked ObjectId                    // Sort them                    KeyValuePair<ObjectId, double> kvp = distances.OrderBy(x => x.Value).ElementAt(0);                    DBObject found = tr.GetObject((ObjectId)kvp.Key, OpenMode.ForRead, false);                    Polyline pl = found as Polyline;                    ed.WriteMessage("\nПлощадь полигона:\t{0:f4}\n", pl.Area);                    pl.UpgradeOpen();                    pl.ColorIndex = 1;                    pl.DowngradeOpen();                    tr.Commit();                    ed.Regen();                }            }            catch (Autodesk.AutoCAD.Runtime.Exception ex)            {                Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage                    (ex.Message + "\n" + ex.StackTrace);                return;            }            finally            {                distances = null;// optional            }        }`
\\\"Always drink upstream from the herd."\\\ - Will Rogers, was died in 1935 plane crash

--> Donate to TheSwamp <--

TT

• Swamp Rat
• Posts: 826
Re: Find loop by click
« Reply #4 on: January 18, 2013, 07:34:03 am »
FYI,
Code: [Select]
`    <snip>`

I think you don't understand what the OP was aking for.

The polyline whose edge is closest to the picked point is not the same as the polyline that contains the picked point.

TT

• Swamp Rat
• Posts: 826
Re: Find loop by click
« Reply #5 on: January 18, 2013, 08:42:42 am »
And here's the 'lightweight' method  :

Code - C#: [Select]
`/// BrepSamples.cs (excerpt)  Copyright (c) 2008  Tony Tanzillo using System;using System.Collections.Generic;using System.Linq;using System.Text;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.BoundaryRepresentation; // acdbmgdbrep.dllusing Autodesk.AutoCAD.Geometry;using AcBr = Autodesk.AutoCAD.BoundaryRepresentation;using ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus; namespace BRepSamples{   public static class Containment   {      /// <summary>      ///       /// This sample shows how to use the BREP API to do      /// simple topographical point/boundary classification      /// on regions.      ///       /// This code requires a reference to AcDbMgdBrep.dll      ///       /// The sample prompts for a region object; gets a BRep       /// object representing the region; and then repeatedly      /// prompts for points, and computes and displays the      /// containment (inside, outside, or on an edge) of each      /// selected point.      ///       /// A point within an inner loop is considered to be       /// 'ouside' the region's area, and so if you perform       /// this operation on a complex region with inner loops,       /// points contained within an inner loop will yield a      /// result of PointContainment.Outside.      //      /// This is the most robust means of determining if a      /// point lies within the implied boundary formed by a      /// collection of AutoCAD entites that can be used to       /// create a valid AutoCAD Region object, and is the       /// preferred means of doing simple point containment       /// testing.      ///       /// You can adapt this code to perform containment tests      /// on various AutoCAD entities such as closed polylines,       /// splines, ellipses, etc., by generating a region from       /// the closed geometry, using it to do the containment       /// test, and then disposing it without having to add it      /// to a database.      ///       /// Note that the sample code was designed to work with      /// regions that lie in the WCS XY plane and points that       /// lie in the same plane.       ///       /// </summary>       [CommandMethod( "CONTAINMENT" )]      public static void ContainmentCommand()      {         Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;         ObjectId id = GetRegion( ed, "\nSelect a region: " );         if( !id.IsNull )         {            using( Transaction tr = ed.Document.TransactionManager.StartTransaction() )            {               try               {                  Region region = id.GetObject( OpenMode.ForRead ) as Region;                   PromptPointOptions ppo = new PromptPointOptions( "\nSelect a point: " );                  ppo.AllowNone = true;                   while( true ) // loop while user continues to pick points                  {                     /// Get a point from user:                     PromptPointResult ppr = ed.GetPoint( ppo );                      if( ppr.Status != PromptStatus.OK )  // no point was selected, exit                        break;                      // use the GetPointContainment helper method below to                      // get the PointContainment of the selected point:                     PointContainment containment = GetPointContainment( region, ppr.Value );                      // Display the result:                     ed.WriteMessage( "\nPointContainment = {0}", containment.ToString() );                  }               }               finally               {                  tr.Commit();               }            }         }      }       /// <summary>      ///       /// This variation performs point contianment testing      /// against any closed curve from which a simple region       /// (e.g., no inner-loops) can be genreated, using the      /// Region.CreateFromCurves() API.      ///       /// </summary>       [CommandMethod( "CURVECONTAINMENT" )]      public static void CurveContainmentCommand()      {         Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;         ObjectId id = GetCurve( ed, "\nSelect a closed Curve: " );         if( !id.IsNull )         {            using( Transaction tr = ed.Document.TransactionManager.StartTransaction() )            {               try               {                  Curve curve = (Curve) id.GetObject( OpenMode.ForRead );                  if( !curve.Closed )                  {                     ed.WriteMessage( "\nInvalid selection, requires a CLOSED curve" );                     return;                  }                   using( Region region = RegionFromClosedCurve( curve ) )                  {                     PromptPointOptions ppo = new PromptPointOptions( "\nSelect a point: " );                     ppo.AllowNone = true;                      while( true ) // loop while user continues to pick points                     {                        /// Get a point from user:                        PromptPointResult ppr = ed.GetPoint( ppo );                         if( ppr.Status != PromptStatus.OK )  // no point was selected, exit                           break;                         // use the GetPointContainment helper method below to                         // get the PointContainment of the selected point:                        PointContainment containment = GetPointContainment( region, ppr.Value );                         // Display the result:                        ed.WriteMessage( "\nPointContainment = {0}", containment.ToString() );                     }                  }               }               finally               {                  tr.Commit();               }            }         }      }        // this helper method takes a Region and a Point3d that must be      // in the plane of the region, and returns the PointContainment      // of the given point, adjusted to correctly indicate its actual      // relationship to the region (inside/on boundary edge/outside).       public static PointContainment GetPointContainment( Region region, Point3d point )      {         PointContainment result = PointContainment.Outside;          /// Get a Brep object representing the region:         using( Brep brep = new Brep( region ) )         {            if( brep != null )            {               // Get the PointContainment and the BrepEntity at the given point:                using( BrepEntity ent = brep.GetPointContainment( point, out result ) )               {                  /// GetPointContainment() returns PointContainment.OnBoundary                  /// when the picked point is either inside the region's area                  /// or exactly on an edge.                  ///                   /// So, to distinguish between a point on an edge and a point                  /// inside the region, we must check the type of the returned                  /// BrepEntity:                  ///                   /// If the picked point was on an edge, the returned BrepEntity                   /// will be an Edge object. If the point was inside the boundary,                  /// the returned BrepEntity will be a Face object.                  //                  /// So if the returned BrepEntity's type is a Face, we return                  /// PointContainment.Inside:                   if( ent is AcBr.Face )                     result = PointContainment.Inside;                }            }         }         return result;      }       public static Region RegionFromClosedCurve( Curve curve )      {         if( !curve.Closed )            throw new ArgumentException( "Curve must be closed." );         DBObjectCollection curves = new DBObjectCollection();         curves.Add( curve );         using( DBObjectCollection regions = Region.CreateFromCurves( curves ) )         {            if( regions == null || regions.Count == 0 )               throw new InvalidOperationException( "Failed to create regions" );            if( regions.Count > 1 )               throw new InvalidOperationException( "Multiple regions created" );            return regions.Cast<Region>().First();         }      }       public static PointContainment GetPointContainment( Curve curve, Point3d point )      {         if( ! curve.Closed )            throw new ArgumentException("Curve must be closed.");         Region region = RegionFromClosedCurve( curve );         if( region == null )            throw new InvalidOperationException( "Failed to create region" );         using( region )         {            return GetPointContainment( region, point );         }      }       public static ObjectId GetCurve( Editor editor, string msg )      {         PromptEntityOptions peo = new PromptEntityOptions( msg );         peo.SetRejectMessage( "Invalid selection: requires a closed Curve," );         peo.AddAllowedClass( typeof( Curve ), false );         PromptEntityResult res = editor.GetEntity( peo );         return res.Status == PromptStatus.OK ? res.ObjectId : ObjectId.Null;      }       public static ObjectId GetRegion( Editor editor, string msg )      {         PromptEntityOptions peo = new PromptEntityOptions( msg );         peo.SetRejectMessage( "Invalid selection: requires a region," );         peo.AddAllowedClass( typeof( Region ), false );         PromptEntityResult res = editor.GetEntity( peo );         return res.Status == PromptStatus.OK ? res.ObjectId : ObjectId.Null;      }    }} `
« Last Edit: January 18, 2013, 08:55:19 am by TT »

fixo

• Swamp Rat
• Posts: 856
• My location is not in the Hitleropa
Re: Find loop by click
« Reply #6 on: January 18, 2013, 09:41:22 am »

I think you don't understand what the OP was aking for.

The polyline whose edge is closest to the picked point is not the same as the polyline that contains the picked point.
inside the closed not-intersected polygons btw
\\\"Always drink upstream from the herd."\\\ - Will Rogers, was died in 1935 plane crash

--> Donate to TheSwamp <--

dinekro

• Mosquito
• Posts: 3
Re: Find loop by click
« Reply #7 on: January 18, 2013, 04:31:08 pm »
And here's the 'lightweight' method  :

Thank You very much!
The code is very useful
I think at the first blush, BrepEntity is very powerful thing for this case!

It is interesting that BrepEntity hasn't GetPointContainment's method in Objectarx Help (arxmgd.chm) It is strangely...

FYI,
<snip>
Thank You too, I think, I will try utilizing its code...

« Last Edit: January 18, 2013, 04:52:52 pm by dinekro »

cnicho

• Mosquito
• Posts: 14
Re: Find loop by click
« Reply #8 on: March 07, 2014, 06:26:05 am »
I know this is an 'old' post but thanks for the Brep sample code Tony. Very helpful.

Regards,
Craig

bebeto

• Mosquito
• Posts: 1
Re: Find loop by click
« Reply #9 on: September 30, 2014, 09:50:58 am »
Hi,

This method is valid to Regions... but I need a method to check if a point is into a polyline...

Some ideas???

gile

• Water Moccasin
• Posts: 1834
• Marseille, France
Re: Find loop by click
« Reply #10 on: September 30, 2014, 10:40:18 am »
Welcome bebeto,

If you attentively read the code, you'd see a RegionFromClosedCurve() method is used in the CURVECONTAINMENT command so that it works with any closed curve.
Speaking English as a French Frog