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

0 Members and 1 Guest are viewing this topic.

dinekro

  • Guest
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?

Thanks for Your Help!
« Last Edit: January 16, 2013, 04:53:58 PM by dinekro »

kaefer

  • Guest
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

  • Guest
Re: Find loop by click
« Reply #2 on: January 17, 2013, 01:52:20 PM »
Thanks for your answer!

"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

  • Guest
Re: Find loop by click
« Reply #3 on: January 17, 2013, 05:04:00 PM »
I've answered on native forum
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
            }
        }

TheMaster

  • Guest
Re: Find loop by click
« Reply #4 on: January 18, 2013, 07:34:03 AM »
I've answered on native forum
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. 


TheMaster

  • Guest
Re: Find loop by click
« Reply #5 on: January 18, 2013, 08:42:42 AM »
And here's the 'lightweight' method  :laugh::

Code - C#: [Select]
  1. /// BrepSamples.cs (excerpt)  Copyright (c) 2008  Tony Tanzillo
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.EditorInput;
  9. using Autodesk.AutoCAD.ApplicationServices;
  10. using Autodesk.AutoCAD.DatabaseServices;
  11. using Autodesk.AutoCAD.BoundaryRepresentation; // acdbmgdbrep.dll
  12. using Autodesk.AutoCAD.Geometry;
  13. using AcBr = Autodesk.AutoCAD.BoundaryRepresentation;
  14. using ErrorStatus = Autodesk.AutoCAD.Runtime.ErrorStatus;
  15.  
  16. namespace BRepSamples
  17. {
  18.    public static class Containment
  19.    {
  20.       /// <summary>
  21.       ///
  22.       /// This sample shows how to use the BREP API to do
  23.       /// simple topographical point/boundary classification
  24.       /// on regions.
  25.       ///
  26.       /// This code requires a reference to AcDbMgdBrep.dll
  27.       ///
  28.       /// The sample prompts for a region object; gets a BRep
  29.       /// object representing the region; and then repeatedly
  30.       /// prompts for points, and computes and displays the
  31.       /// containment (inside, outside, or on an edge) of each
  32.       /// selected point.
  33.       ///
  34.       /// A point within an inner loop is considered to be
  35.       /// 'ouside' the region's area, and so if you perform
  36.       /// this operation on a complex region with inner loops,
  37.       /// points contained within an inner loop will yield a
  38.       /// result of PointContainment.Outside.
  39.       //
  40.       /// This is the most robust means of determining if a
  41.       /// point lies within the implied boundary formed by a
  42.       /// collection of AutoCAD entites that can be used to
  43.       /// create a valid AutoCAD Region object, and is the
  44.       /// preferred means of doing simple point containment
  45.       /// testing.
  46.       ///
  47.       /// You can adapt this code to perform containment tests
  48.       /// on various AutoCAD entities such as closed polylines,
  49.       /// splines, ellipses, etc., by generating a region from
  50.       /// the closed geometry, using it to do the containment
  51.       /// test, and then disposing it without having to add it
  52.       /// to a database.
  53.       ///
  54.       /// Note that the sample code was designed to work with
  55.       /// regions that lie in the WCS XY plane and points that
  56.       /// lie in the same plane.
  57.       ///
  58.       /// </summary>
  59.  
  60.       [CommandMethod( "CONTAINMENT" )]
  61.       public static void ContainmentCommand()
  62.       {
  63.          Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  64.          ObjectId id = GetRegion( ed, "\nSelect a region: " );
  65.          if( !id.IsNull )
  66.          {
  67.             using( Transaction tr = ed.Document.TransactionManager.StartTransaction() )
  68.             {
  69.                try
  70.                {
  71.                   Region region = id.GetObject( OpenMode.ForRead ) as Region;
  72.  
  73.                   PromptPointOptions ppo = new PromptPointOptions( "\nSelect a point: " );
  74.                   ppo.AllowNone = true;
  75.  
  76.                   while( true ) // loop while user continues to pick points
  77.                   {
  78.                      /// Get a point from user:
  79.                      PromptPointResult ppr = ed.GetPoint( ppo );
  80.  
  81.                      if( ppr.Status != PromptStatus.OK )  // no point was selected, exit
  82.                         break;
  83.  
  84.                      // use the GetPointContainment helper method below to
  85.                      // get the PointContainment of the selected point:
  86.                      PointContainment containment = GetPointContainment( region, ppr.Value );
  87.  
  88.                      // Display the result:
  89.                      ed.WriteMessage( "\nPointContainment = {0}", containment.ToString() );
  90.                   }
  91.                }
  92.                finally
  93.                {
  94.                   tr.Commit();
  95.                }
  96.             }
  97.          }
  98.       }
  99.  
  100.       /// <summary>
  101.       ///
  102.       /// This variation performs point contianment testing
  103.       /// against any closed curve from which a simple region
  104.       /// (e.g., no inner-loops) can be genreated, using the
  105.       /// Region.CreateFromCurves() API.
  106.       ///
  107.       /// </summary>
  108.  
  109.       [CommandMethod( "CURVECONTAINMENT" )]
  110.       public static void CurveContainmentCommand()
  111.       {
  112.          Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  113.          ObjectId id = GetCurve( ed, "\nSelect a closed Curve: " );
  114.          if( !id.IsNull )
  115.          {
  116.             using( Transaction tr = ed.Document.TransactionManager.StartTransaction() )
  117.             {
  118.                try
  119.                {
  120.                   Curve curve = (Curve) id.GetObject( OpenMode.ForRead );
  121.                   if( !curve.Closed )
  122.                   {
  123.                      ed.WriteMessage( "\nInvalid selection, requires a CLOSED curve" );
  124.                      return;
  125.                   }
  126.  
  127.                   using( Region region = RegionFromClosedCurve( curve ) )
  128.                   {
  129.                      PromptPointOptions ppo = new PromptPointOptions( "\nSelect a point: " );
  130.                      ppo.AllowNone = true;
  131.  
  132.                      while( true ) // loop while user continues to pick points
  133.                      {
  134.                         /// Get a point from user:
  135.                         PromptPointResult ppr = ed.GetPoint( ppo );
  136.  
  137.                         if( ppr.Status != PromptStatus.OK )  // no point was selected, exit
  138.                            break;
  139.  
  140.                         // use the GetPointContainment helper method below to
  141.                         // get the PointContainment of the selected point:
  142.                         PointContainment containment = GetPointContainment( region, ppr.Value );
  143.  
  144.                         // Display the result:
  145.                         ed.WriteMessage( "\nPointContainment = {0}", containment.ToString() );
  146.                      }
  147.                   }
  148.                }
  149.                finally
  150.                {
  151.                   tr.Commit();
  152.                }
  153.             }
  154.          }
  155.       }
  156.  
  157.  
  158.       // this helper method takes a Region and a Point3d that must be
  159.       // in the plane of the region, and returns the PointContainment
  160.       // of the given point, adjusted to correctly indicate its actual
  161.       // relationship to the region (inside/on boundary edge/outside).
  162.  
  163.       public static PointContainment GetPointContainment( Region region, Point3d point )
  164.       {
  165.          PointContainment result = PointContainment.Outside;
  166.  
  167.          /// Get a Brep object representing the region:
  168.          using( Brep brep = new Brep( region ) )
  169.          {
  170.             if( brep != null )
  171.             {
  172.                // Get the PointContainment and the BrepEntity at the given point:
  173.  
  174.                using( BrepEntity ent = brep.GetPointContainment( point, out result ) )
  175.                {
  176.                   /// GetPointContainment() returns PointContainment.OnBoundary
  177.                   /// when the picked point is either inside the region's area
  178.                   /// or exactly on an edge.
  179.                   ///
  180.                   /// So, to distinguish between a point on an edge and a point
  181.                   /// inside the region, we must check the type of the returned
  182.                   /// BrepEntity:
  183.                   ///
  184.                   /// If the picked point was on an edge, the returned BrepEntity
  185.                   /// will be an Edge object. If the point was inside the boundary,
  186.                   /// the returned BrepEntity will be a Face object.
  187.                   //
  188.                   /// So if the returned BrepEntity's type is a Face, we return
  189.                   /// PointContainment.Inside:
  190.  
  191.                   if( ent is AcBr.Face )
  192.                      result = PointContainment.Inside;
  193.  
  194.                }
  195.             }
  196.          }
  197.          return result;
  198.       }
  199.  
  200.       public static Region RegionFromClosedCurve( Curve curve )
  201.       {
  202.          if( !curve.Closed )
  203.             throw new ArgumentException( "Curve must be closed." );
  204.          DBObjectCollection curves = new DBObjectCollection();
  205.          curves.Add( curve );
  206.          using( DBObjectCollection regions = Region.CreateFromCurves( curves ) )
  207.          {
  208.             if( regions == null || regions.Count == 0 )
  209.                throw new InvalidOperationException( "Failed to create regions" );
  210.             if( regions.Count > 1 )
  211.                throw new InvalidOperationException( "Multiple regions created" );
  212.             return regions.Cast<Region>().First();
  213.          }
  214.       }
  215.  
  216.       public static PointContainment GetPointContainment( Curve curve, Point3d point )
  217.       {
  218.          if( ! curve.Closed )
  219.             throw new ArgumentException("Curve must be closed.");
  220.          Region region = RegionFromClosedCurve( curve );
  221.          if( region == null )
  222.             throw new InvalidOperationException( "Failed to create region" );
  223.          using( region )
  224.          {
  225.             return GetPointContainment( region, point );
  226.          }
  227.       }
  228.  
  229.       public static ObjectId GetCurve( Editor editor, string msg )
  230.       {
  231.          PromptEntityOptions peo = new PromptEntityOptions( msg );
  232.          peo.SetRejectMessage( "Invalid selection: requires a closed Curve," );
  233.          peo.AddAllowedClass( typeof( Curve ), false );
  234.          PromptEntityResult res = editor.GetEntity( peo );
  235.          return res.Status == PromptStatus.OK ? res.ObjectId : ObjectId.Null;
  236.       }
  237.    
  238.       public static ObjectId GetRegion( Editor editor, string msg )
  239.       {
  240.          PromptEntityOptions peo = new PromptEntityOptions( msg );
  241.          peo.SetRejectMessage( "Invalid selection: requires a region," );
  242.          peo.AddAllowedClass( typeof( Region ), false );
  243.          PromptEntityResult res = editor.GetEntity( peo );
  244.          return res.Status == PromptStatus.OK ? res.ObjectId : ObjectId.Null;
  245.       }
  246.  
  247.    }
  248. }
  249.  
« Last Edit: January 18, 2013, 08:55:19 AM by TT »

fixo

  • Guest
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.
On native forum he asked about how to click points
inside the closed not-intersected polygons btw

dinekro

  • Guest
Re: Find loop by click
« Reply #7 on: January 18, 2013, 04:31:08 PM »
And here's the 'lightweight' method  :laugh::

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...

I've answered on native forum
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

  • Guest
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.


bebeto

  • Guest
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

  • Gator
  • Posts: 2507
  • 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