Hi there!
I'm trying to find a solution for the following task:
I have to programmatically select some entities based on a predefined algorithm (It doesent matter which one, but to be clear e.g. Every 2nd block of a given name in x-direction between two points). Then I have to highlight them and let the user add other entities to, or remove preselected entities from this selection set by selecting them on screen.
I tried to prototype it in LISP like this, but failed, because I can't control what the user selects with this method:
(setq PreSs (ComputePreSelection))
(command "_.SELECT")
(if PreSs (command PreSs))
(while (> (getvar "CMDACTIVE") 0) (command pause))
(setq SelSet (ssget "_P")
I also failed using this code, because I can't deselect the preselected entities while looping in the filtering ssget call:
(setq PreSs (ComputePreSelection))
(command "_.SELECT")
(if PreSs (command PreSs))
(command (ssget '(( 0 . "INSERT") (8 . "S_ON*"))))
(setq SelSet (ssget "_P")
Is there a possibility using .NET to pass a predefined selection set to a selection method and let it modify while taking a filter into account?
TIA - Martin
Solved! Go to Solution.
Solved by DECH0002. Go to Solution.
Solved by moogalm. Go to Solution.
Hi Martin ,
One way of doing in .NET is use SetImpliedSelection API ,which preselects the entities which are passed to the API , it accepts ObjectID array.
So you can programatically popuplate the array.
Code Sample :
public static void FilterSelectionSet() { // Get the current document editor Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Document doc = Application.DocumentManager.MdiActiveDocument; /*To Illustrate I'm getting an Object Id of single blockreference*/ ObjectIdCollection idColl = new ObjectIdCollection(); Handle blockHandle = new Handle(0x350); ObjectId id1 = ObjectId.Null; if (doc.Database.TryGetObjectId(blockHandle, out id1)) { idColl.Add(id1); } ObjectId[] idArray = new ObjectId[idColl.Count]; idColl.CopyTo(idArray, 0); /*This imply the selection of block reference and user can deselect later*/ ed.SetImpliedSelection(idArray); }
@DECH0002 wrote:
I have to programmatically select some entities based on a predefined algorithm (It doesent matter which one, but to be clear e.g. Every 2nd block of a given name in x-direction between two points). Then I have to highlight them and let the user add other entities to, or remove preselected entities from this selection set by selecting them on screen.
I tried to prototype it in LISP like this, but failed, because I can't control what the user selects with this method:
(setq PreSs (ComputePreSelection))
(command "_.SELECT")
(if PreSs (command PreSs))
(while (> (getvar "CMDACTIVE") 0) (command pause))
(setq SelSet (ssget "_P")
I also failed using this code, because I can't deselect the preselected entities while looping in the filtering ssget call:
(setq PreSs (ComputePreSelection))
(command "_.SELECT")
(if PreSs (command PreSs))
(command (ssget '(( 0 . "INSERT") (8 . "S_ON*"))))
(setq SelSet (ssget "_P")
...
FWIW - To 'select' entities in LISP, simply pass your selection set to the SSSETFIRST LispFunction.
Cheers
"How we think determines what we do, and what we do determines what we get."
Hi Madhukar
Unfortunately, the proposed solution does not, what I expect, no matter if I use the LISP or the .NET variant.
What happens depends on the setting of PICKFIRST. If PICKFIRST is 1, then the subsequent call to Editor.GetSelection immediately returns the selection set I passed to SetImpliedSelection. If PICKFIRST is 0, the entities I pass to SetImpliedSelection are highlighted with GRIPS and I can select objects mannually. However, deselecting entities from the implied selection set is not possible. But this is, what I described in my topic title with "Preselect Selectionset Programmatically And Let User Modify It". EDIT: Probably there is an option of GetSelection, I missed???
Let me tell in detail, what I have to achieve:
I'm developing an application for railway switch and track planning. Switches and tracks have a hughe amount of sleepers. Now, we have the need to select multiple sleepers along a rail. Because sleepers are displayed in dashed lines sometimes, the fence selection method fails too often because of its lack to select objects when the rubber band crosses a dashed line at one of its gaps.
In addition, we have the need to select only every 2nd, 3rd (a.s.o.) sleeper fore some actions. So we implemented a selection method which lets the user select a rail (which is a polyline in fact), a start point, and an end point. The method will now select those sleepers which are crossing the rail and are matching the interval condition. Until here, everything works fine. We have an array of ObjectId's or selection set of preselected entities (sleepers).
Now, the user should be eligible to add, or remove individual sleepers to, or from the selection set. That said, he must be able to modify the programmatically preselected selection. And, to make things more complex, he must be eligible only to add new sleepers (which are blocks, in fact), nothing else.
I hope my explanations are understandable. Thank you for your further investigation.
Regards,
Martin
EDIT: I do not necessarely need a .NET API solution. LISP can do as well.
Hi
Thanks for your detailed explanation ,this helped a lot .
I'm assuming you are working on .NET 4.5 and AutoCAD 2015 SP2.
Sorry I have more expertise on .NET than LISP.
I have attached code snippet and test with pickfirst =0 &=1 I see no difference, in the preselection.
In the attached video link I have demonstrated deselecting entities and selecting entities too.
I have attached my test DWG too.
public static SelectionSet ss; // Modal Command with pickfirst selection [CommandMethod("MyGroup", "MyPickFirst", "MyPickFirstLocal", CommandFlags.Modal | CommandFlags.UsePickSet)] async public void MyPickFirst() // This method can have any name { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.PromptedForSelection+=ed_PromptedForSelection; try { await ed.CommandAsync("_.Select"); ObjectIdCollection idColl = new ObjectIdCollection(); // Preselect 350, 3AE Handle h1 = new Handle(0X350); ObjectId id1 = ObjectId.Null; if (doc.Database.TryGetObjectId(h1, out id1)) { idColl.Add(id1); } Handle h2 = new Handle(0X3AE); ObjectId id2 = ObjectId.Null; if (doc.Database.TryGetObjectId(h2, out id2)) { idColl.Add(id2); } /*You can populate array*/ ObjectId[] ids = new ObjectId[idColl.Count]; idColl.CopyTo(ids, 0); ss = SelectionSet.FromObjectIds(ids); await ed.CommandAsync(ss, Editor.PauseToken); // Let the user select / unselect more entities. // To unselect use shift+click } catch (Autodesk.AutoCAD.Runtime.Exception) { ed.WriteMessage("Exception thrown."); } } private void ed_PromptedForSelection(object sender, PromptSelectionResultEventArgs e) { Editor ed = sender as Editor; PromptSelectionResult result = e.Result; if (result.Status != PromptStatus.OK) { ed.PromptedForSelection -= ed_PromptedForSelection; return; } ss = result.Value; Application.ShowAlertDialog("Number of objects selected: " + ss.Count.ToString()); foreach (ObjectId id in ss.GetObjectIds()) { ed.WriteMessage("selected Objects are {0} \n", id.Handle.ToString()); } }
Hello Madhukar
Thank you for your great work. Since I'm still working with AutoCAD 2014/.NET4, it's not directly useable for me, since CommandAsync was introduced with Release 2015. However, I appreciate your solution, since it shows us how to achieve this in future releases, and it pointed me to the solution for the current release.
Just wanted to add a hint for future readers: The request to filter the user selected entities can be achieved by adding a SelectionAdded event handler to the Editor instance.
For now, I'll solve this request in a mixed LISP/.NET implementation like folows:
(setq ps (eacl_acquireSleepers)) ; Preselect some sleepers according the algorithm described in a previous post. (eacl_sethandler) ; attach a SelectionAdded event handler in a .NET-implemented lisp function. (command "_.SELECT" ps) ; Add the preselected sleepers to the selection set, so they are highlighted. (while (> (getvar "CMDACTIVE") 0) ; Let the user add or remove entities as long as the user does not presses enter. (command pause) ) (setq ss (ssget "_P")) ; Get the selected sleepers.
(eacl_removehandler) ; detach the SelectionAdded event handler in a .NET-implemented lisp function.
The .NET part will look like this:
[LispFunction("eacl_sethandler")] public void SetHandler(ResultBuffer args) // This method can have any name { // Put your command code here Document doc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.SelectionAdded += EditorOnSelectionAdded; }
[LispFunction("eacl_removehandler")] public void RemoveHandler(ResultBuffer args) // This method can have any name { // Put your command code here Document doc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.SelectionAdded -= EditorOnSelectionAdded; } private void EditorOnSelectionAdded(object sender, SelectionAddedEventArgs selectionAddedEventArgs) { var ed = (Editor) sender; using (Transaction tr = ed.Document.TransactionManager.StartOpenCloseTransaction()) { for (int i = 0; i < selectionAddedEventArgs.AddedObjects.Count; i++) { SelectedObject addedObject = selectionAddedEventArgs.AddedObjects[i]; var curEnt = tr.GetObject(addedObject.ObjectId, OpenMode.ForRead) as Entity; if (curEnt != null) { if (!(curEnt.Layer.Equals("MySpecificLayer") && curEnt.GetType() == typeof (BlockReference))) { selectionAddedEventArgs.Remove(i); } } else { ed.WriteMessage("\nPanic!"); } } } }
Disclaimer: This code was not tested at all and might contain errors. And of course, I'll make that more generic and robust than shown above but as a starting point for others, it should satisfy.
Good Solution!
But now I have another problem, I need to use the selection with Keywords and I need to change the prompt message. Is it possible?
Thanks.
"How we think determines what we do, and what we do determines what we get."