Author Topic: PointMonitor troubles  (Read 8258 times)

0 Members and 1 Guest are viewing this topic.

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
PointMonitor troubles
« on: August 03, 2014, 01:27:18 PM »
I have a command, for use in Civil3D, which utilizes the PointMonitor event to track whether the cursor is within a certain area of a corridor. A corridor can be made of one to many 'regions', at the onset of the command I loop through all of these regions and create a collection of Polylines and the C3D BaselineRegion objects. The polylines I create from the calculated boundaries of each of these BaselineRegions in order to use in a TransietGraphicsDisplay to give the user a visual indicator that they are within one of the corridor regions. (This mimics the native C3D selection process.)

All of that is working just dandy, as long as there are less than 10, or so, regions. As the number of regions increase, the cursor begins to slow to a crawl across the screen, finally unable to keep up and will jump around. What I'm asking here, I guess, is there a better way of going about this? I'm sure there must be, because doing the same thing with the native C3D commands is instantaneous & smooth.

Here's a snippet of what I'm using. Not showing how I acquire the Polylines, but that portion of code is plenty quick. Also, I use the Brep (BoundaryRepresention) methods to get the IsPointInside value.
Code - C#: [Select]
  1.        private void corridorPointMonitor(object sender, PointMonitorEventArgs e)
  2.         {
  3.             Point3d curpt = e.Context.RawPoint;
  4.  
  5.             foreach (KeyValuePair<Polyline, BaselineRegion> pair in regions)
  6.             {                
  7.                 if(pair.Key.IsPointInside(curpt))
  8.                 {
  9.                     foundRegion = pair.Key;
  10.                     if (previousRegion == null)
  11.                         previousRegion = foundRegion;
  12.                     else if (previousRegion == foundRegion)
  13.                         break;
  14.                     else
  15.                         previousRegion = foundRegion;
  16.                     foundBLRegion = pair.Value;
  17.                     HighlightRegion(pair.Key);
  18.                     break;
  19.                 }
  20.                 else
  21.                 {
  22.                     ClearMarkers();
  23.                     foundRegion = null;
  24.                     previousRegion = null;
  25.                     foundBLRegion = null;
  26.                 }
  27.             }
  28.  
  29.         }
  30.  
  31.         private void HighlightRegion(Polyline region)
  32.         {
  33.             ClearMarkers();          
  34.             region.ColorIndex = 5;
  35.             aGi.TransientManager.CurrentTransientManager.AddTransient(region, aGi.TransientDrawingMode.DirectShortTerm, 128, intColl);
  36.             markers.Add(region);
  37.         }
  38.  

BlackBox

  • King Gator
  • Posts: 3770
Re: PointMonitor troubles
« Reply #1 on: August 03, 2014, 05:12:50 PM »
Perhaps instead of repeatedly determining which Polyline is to be highlighted, etc you instead highlight all from onset, and simply disable visibility of those not to be highlighted.

Also, curious to know if LINQ's OrderBy() Method would be of any help here?
"How we think determines what we do, and what we do determines what we get."

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: PointMonitor troubles
« Reply #2 on: August 03, 2014, 06:36:21 PM »
The polylines are not database resident, just temporary objects to use to determine if the cursor is within them and to pass to the TransientGraphics to display the boundary of a region. I just tested this using Linq, commenting out the code to display the transientgraphics, and it is still very sluggish. (Tested with the temp graphics, too, to insure it is getting the correct region.)
Code - C#: [Select]
  1.         private void corridorPointMonitor(object sender, PointMonitorEventArgs e)
  2.         {
  3.             Point3d curpt = e.Context.RawPoint;
  4.             KeyValuePair<Polyline, BaselineRegion> pair = regions.FirstOrDefault(region => region.Key.IsPointInside(curpt));
  5.             if (pair.Key != null)
  6.             {
  7.                 foundRegion = pair.Key;
  8.                 foundBLRegion = pair.Value;
  9.                 //HighlightRegion(pair.Key);
  10.             }
  11.             else
  12.             {
  13.                 //ClearMarkers();
  14.                 foundRegion = null;
  15.                 foundBLRegion = null;
  16.             }
  17.         }
  18.  
So all I can figure now is that my extension method IsPointInside is where the bottleneck is.

LE3

  • Guest
Re: PointMonitor troubles
« Reply #3 on: August 03, 2014, 06:50:39 PM »
I just tried some old code, and the same issue appears to happen when I have 2300+ closed polylines, (what it is the sysvar to turn off the automatic highlight that autocad does? maybe this has something todo no idea)

Code - C#: [Select]
  1.         private static void OnPointMonitor(object sender, PointMonitorEventArgs e)
  2.         {
  3.             var doc = AcadApp.DocumentManager.MdiActiveDocument;
  4.             _pPoly.Unhighlight();
  5.             _objTrackedPt = e.Context.RawPoint;
  6.             if (!VisCursor(_objTrackedPt)) return;
  7.             //using (doc.LockDocument())
  8.             //{
  9.                 using (var transaction = doc.TransactionManager.StartTransaction())
  10.                 {
  11.                     foreach (ObjectId id in ClosedPolys)
  12.                     {
  13.                         if (IsPointInside(_objTrackedPt, id))
  14.                         {
  15.                             _pPoly = transaction.GetObject(id, OpenMode.ForRead, false) as Polyline;
  16.                             if (_pPoly != null)
  17.                             {
  18.                                 _pPoly.Highlight();
  19.                                 var area = Converter.DistanceToString(_pPoly.Area);
  20.                                 e.AppendToolTipText(string.Format("Area: {0}\nSomething here: \nSomething more: \nAnd at the bottom: ", area));
  21.                             }
  22.                             break;
  23.                         }
  24.                         _pPoly.Unhighlight();
  25.                     }
  26.                     transaction.Commit();
  27.                 }
  28.             //}
  29.         }
  30.  
« Last Edit: August 03, 2014, 06:57:56 PM by LE »

Chumplybum

  • Newt
  • Posts: 97
Re: PointMonitor troubles
« Reply #4 on: August 03, 2014, 07:05:19 PM »
Not sure if this is the one, but PREVIEWFILTER 'excludes specified objects from selection previewing'... if you go to options, then the selection tab, and hover over the various options a tooltip shows up and this usually has the associated variable name. You might want to take a look at the SELECTIONPREVIEWLIMIT variable also

Cheers, Mark

LE3

  • Guest
Re: PointMonitor troubles
« Reply #5 on: August 03, 2014, 08:13:55 PM »
I think it will required the use of a KD-Tree or something similar...

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: PointMonitor troubles
« Reply #6 on: August 03, 2014, 09:43:16 PM »
I think it will required the use of a KD-Tree or something similar...
I think you are right, Luis. Unfortunately, this is something I've tried to learn before and failed miserably...this old brain just struggles trying to comprehend it. I will, however, take another stab at it since the wife is away for a week. Perhaps the quietness around here will be conducive to learning.

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: PointMonitor troubles
« Reply #7 on: August 03, 2014, 10:09:19 PM »
Here's a Screencast showing how it is supposed to work, and then how mine does work, oh, so, slowly:

LE3

  • Guest
Re: PointMonitor troubles
« Reply #8 on: August 04, 2014, 09:56:47 AM »
I think it will required the use of a KD-Tree or something similar...
I think you are right, Luis. Unfortunately, this is something I've tried to learn before and failed miserably...this old brain just struggles trying to comprehend it. I will, however, take another stab at it since the wife is away for a week. Perhaps the quietness around here will be conducive to learning.

Yes, I recall seeing this in the past, and I end up using a kd-tree class but on a custom object (closedPolygon's) in arx by implementing an input point filter derived from AcEdInputPointFilter.

There is a kd-tree here at theswamp by Paul Kohut in C++ and I guess one by Gile (in C#) if I recall....


LE3

  • Guest
Re: PointMonitor troubles
« Reply #9 on: August 04, 2014, 11:54:03 AM »
Hi Jeff,
Try something like this (I got the chance to tested here on my lunch break and appears to work).

Add a variable for your class to hold the ids of your regions and the geo center point of the region.
Code - C#: [Select]
  1. public static Dictionary<ObjectId, Point3d> IdsAndPoints = new Dictionary<ObjectId, Point3d>();
  2.  


Here it is what I use to calculate the geo center
Code - C#: [Select]
  1.                     using (var transaction = db.TransactionManager.StartTransaction())
  2.                     {
  3.                         foreach (var id in ids)
  4.                         {
  5.                             var polyline = transaction.GetObject(id, OpenMode.ForRead, false) as Polyline;
  6.                             if (polyline == null || !polyline.Closed) continue;
  7.                             var extent = polyline.GeometricExtents;
  8.                             var minPoint = extent.MinPoint;
  9.                             var maxPoint = extent.MaxPoint;
  10.                             var vector = maxPoint - minPoint;
  11.                             var geoCenter = minPoint + vector / 2;
  12.                             IdsAndPoints.Add(id, geoCenter);
  13.                         }
  14.                         transaction.Commit();
  15.                     }
  16.  

Then add a new method to calculate the distance to the points
Code - C#: [Select]
  1.         private static double DistanceFromEachPoint(Point3d source, Point3d target)
  2.         {
  3.             return Math.Pow(target.X - source.X, 2) + Math.Pow(target.Y - source.Y, 2);
  4.         }
  5.  

Then on your point monitor event, add something like this (I did my test using 40 polylines to do the search around the point monitor tracked point, all the polylines in the drawing are 2300+ - all the command line works and also there is no speed down at all)
Code - C#: [Select]
  1.         private static void OnPointMonitor(object sender, PointMonitorEventArgs e)
  2.         {
  3.             var doc = AcadApp.DocumentManager.MdiActiveDocument;
  4.             _pPoly.Unhighlight();
  5.             _objTrackedPt = e.Context.ComputedPoint;
  6.             if (!VisCursor(_objTrackedPt)) return;
  7.             try
  8.             {
  9.                 using (var transaction = doc.TransactionManager.StartTransaction())
  10.                 {
  11.                     try
  12.                     {
  13.                         const int count = 40;
  14.                         var pairs =
  15.                             IdsAndPoints.Where(point => point.Value != _objTrackedPt)
  16.                                         .OrderBy(point => DistanceFromEachPoint(_objTrackedPt, point.Value))
  17.                                         .Take(count);
  18.                         foreach (var pair in pairs)
  19.                         {
  20.                             if (IsPointInside(_objTrackedPt, pair.Key))
  21.                             {
  22.                                 _pPoly = transaction.GetObject(pair.Key, OpenMode.ForRead, false) as Polyline;
  23.                                 if (_pPoly != null)
  24.                                 {
  25.                                     _pPoly.Highlight();
  26.                                     break;
  27.                                 }
  28.                             }
  29.                             else
  30.                             {
  31.                                 if (_pPoly != null)
  32.                                 {
  33.                                     _pPoly.Unhighlight();
  34.                                 }
  35.                             }
  36.                         }
  37.                         transaction.Commit();
  38.                     }
  39.                     finally
  40.                     {
  41.                         transaction.Dispose();
  42.                     }
  43.                 }
  44.             }
  45.             catch{}
  46.         }
  47.  

HTH
« Last Edit: August 04, 2014, 12:17:00 PM by LE »

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: PointMonitor troubles
« Reply #10 on: August 04, 2014, 02:00:49 PM »
Thanks, Luis! I'll give this a try tonight, or possibly during lunch.

LE3

  • Guest
Re: PointMonitor troubles
« Reply #11 on: August 04, 2014, 11:12:06 PM »
Thanks, Luis! I'll give this a try tonight, or possibly during lunch.

Also, you can try using parallelism, tested here and runs better.- just add the AsParallel extension for better performance.
Code - C#: [Select]
  1. var pairs =
  2.                     IdsAndPoints.AsParallel().Where(point => point.Value != _trackedPoint)
  3.                                 .OrderBy(point => DistanceFromEachPoint(_trackedPoint, point.Value))
  4.                                 .Take(countOfItemsOnSearch);
  5.  
« Last Edit: August 04, 2014, 11:15:43 PM by LE »

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: PointMonitor troubles
« Reply #12 on: August 04, 2014, 11:25:59 PM »
After some testing, head banging, testing, breaking things, testing some more, struggling to find out why some Linq methods are refusing to work, etc., I have found that this does help but is not quite working as needed...yet.

First, the Linq issue. using the .Take(count) as LE showed, never returned a Dictionary with a Count > 0. Commenting out the Take then returned all of the regions, in the OrderedBy format, items. In order to use the GeoCenter point and still pass both the Polyline and BaselineRegion objects in the Dictionary, I created a Class to hold those 2 objects. Perhaps the use of a Class object is causing this?

The next Linq issue is similar, in that I tried using this: var pair = pairs.FirstOrDefault(region => region.Value.polyline.IsPointInside(curpt));
which also never returns anything but an empty object.

Finally, after working through those issues and getting code which actually gets me a good KeyValuePair with the Polyline in which the cursor is located, it doesn't always get the one it is in. Due to the funky shaped nature of a Corridor and the regions that comprise it, sometimes the cursor may be close to 10 of the calculated Center points but none of those are the one linked to the polyline the cursor is actually in. Increasing the number of items in the Dictionary to loop through increases the chances of this always working, but also begins to impact the performance again. It is also still a bit slow when the cursor is not within any of the regions.

Since I am seeing a speed improvement with this approach, I think that going with the KD-Tree approach will likewise increase the performance. Off to try to figure out how to implement that.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: PointMonitor troubles
« Reply #13 on: August 05, 2014, 01:32:00 AM »
Hi Jeff,

If you're going with the Kd tree route, you can perhaps get some inspiration from this thread (the last C# version of Point3dTree in reply #26)
Speaking English as a French Frog

LE3

  • Guest
Re: PointMonitor troubles
« Reply #14 on: August 05, 2014, 09:01:01 AM »
That's strange.

Can you upload a test drawing with those funky shapes?

The OrderBy and Take will do the same or similar as in the Kd-Tree do the arrangement of points based on distances (neighbor's) and Take as the search radius.