I'm looking to simply select a corridor feature line through the API. The following code generates an IndexOutOfRangeException.
Has anybody successfully selected a feature line from within a corridor object?
Thanks in advance
Andrew
public static bool SelectValidFeatureLine()
{
// Get the AutoCAD Editor
Editor ed = acApp.Application.DocumentManager.MdiActiveDocument.Editor;
// Select a feature line from a corridor
PromptEntityOptions selFL = new PromptEntityOptions("\nSelect Corridor Feature Line: ");
selFL.SetRejectMessage("\nOnly a Corridor Feature Line Object is allowed.");
selFL.AddAllowedClass(typeof(CorridorFeatureLine), true); // Errors here
PromptEntityResult resFL = ed.GetEntity(selFL);
if (resFL.Status != PromptStatus.OK) return false;
featureID = resFL.ObjectId;
return true;
}
I need the same ... to get the name of the FeaureLine on selecting it when it is part of a Corridor ... I guess that the code Andrew tried will only provide the Parent on GetEntity ... which is the Corridor. ... I guess we can only use the position of the get entity result to discover the name of the FeatureLine .. correct?
selFL.AddAllowedClass(GetType(CorridorFeatureLine), True)
Please disregard my above suggestion. I see the error that you are getting now.
The following worked for me. Sorry its in VB.net.
<CommandMethod("CorFLSelect")> _ Public Sub CorFlSelect() ' This method can have any name Dim aDoc As Document = Application.DocumentManager.MdiActiveDocument Dim ed As Editor = aDoc.Editor Dim db As Database = aDoc.Database Dim Opts As New PromptEntityOptions(vbCrLf + "Select Corridor Feature Line: ") Opts.SetRejectMessage(vbCrLf + "Only a Corridor Feature Line Object is Allowed.") Opts.AddAllowedClass(GetType(FeatureLine), False) Dim res As PromptEntityResult = ed.GetEntity(Opts) If res.Status = PromptStatus.OK Then ed.WriteMessage(vbCrLf + "All is Good in the World.") Else ed.WriteMessage(vbCrLf + "All is NOT Good in the World.") End If End Sub
: ( just tried that ... its not working for me, attached example corridor DWG, feature lines are yellow (Lane Point Code), Named View: 1.
Hip... please attach a Corridor DWG that worked with the code you used. Or .. maybe I have a variable value set incorrectly?
In the OP's original post he was trying to 'AddAllowedClass(typeof(CorridorFeatureLine),true)'. That is where his error was coming up. I suggested that he 'AddAllowedClass(typeof(FeatureLine), false)'. It is becoming apparent to me that the intention is to select a corridor to extract the feature line from the corridor. In which case one should use 'AddAllowedClass(typeof(Corridor), true)'. Then we need to work on what the OP (I'm assuming) originally wanted to achieve, which is to extract the corridor feature line by selecting a corridor. I'll wait for the OP to confirm this.
You are right, I am trying to select a feature line from within a corridor, extract its values and either report the values (or even display them in a profile view).
I have tried the code above, but unfortunately had no luck.
The main problem is that if just select a corridor object, I cannot dig into the corridor to extract the corridorfeatureline object.
Here is some old code from the Civil 3D Reminders Pack. I haven't tested it since Civil 3D 2010. It's in COM, but the .NET version should be similar.
' (C) Copyright 2009 by Christopher Fugitt, Parts modified. ' (C) Copyright 2009 by Autodesk, Inc. ' ' Permission to use, copy, modify, and distribute this software in ' object code form for any purpose and without fee is hereby granted, ' provided that the above copyright notice appears in all copies and ' that both that copyright notice and the limited warranty and ' restricted rights notice below appear in all supporting ' documentation. ' ' AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. ' AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF ' MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. ' DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE ' UNINTERRUPTED OR ERROR FREE. ' ' Use, duplication, or disclosure by the U.S. Government is subject to ' restrictions set forth in FAR 52.227-19 (Commercial Computer ' Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) ' (Rights in Technical Data and Computer Software), as applicable. ' Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.Civil.Roadway.DatabaseServices Imports Autodesk.AECC.Interop.Roadway Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Imports System.Collections.Generic Imports Autodesk.AutoCAD.Geometry Imports Quux.C3DUtilities <Assembly: CommandClass(GetType(XrefCorridor3DpolylineApplication))> Public Class XrefCorridor3DpolylineApplication ' Here's where the heavy lifting happens... <CommandMethod("C3DRXREFCorridor3DPolyline")> _ Public Sub XREFCorridor3DPoly() ' Have the user select a corridor. Try Dim oCivil As New AeccAppConnection Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim sPrompt As String = "Select corridor: " Dim entopts As New PromptEntityOptions(sPrompt) entopts.Message = sPrompt Dim entRes As PromptEntityResult = Nothing Try entRes = ed.GetEntity(entopts) Catch ed.WriteMessage("You did not select a valid entity") End Try If entRes.Status = PromptStatus.OK Then Dim tr As Transaction = db.TransactionManager.StartTransaction() Using tr ' First get the currently selected object ' and check whether it's a block reference Dim objId As ObjectId = entRes.ObjectId Dim br As BlockReference = TryCast(tr.GetObject(objId, OpenMode.ForRead), BlockReference) If br IsNot Nothing Then ' If so, we check whether the block table record ' to which it refers is actually from an XRef Dim btrId As ObjectId = br.BlockTableRecord Dim btr As BlockTableRecord = TryCast(tr.GetObject(btrId, OpenMode.ForRead), BlockTableRecord) If btr IsNot Nothing Then If btr.IsFromExternalReference Then ' If so, then we programmatically select the object ' underneath the pick-point already used Dim pneo As New PromptNestedEntityOptions("") pneo.NonInteractivePickPoint = entRes.PickedPoint pneo.UseNonInteractivePickPoint = True Dim pner As PromptNestedEntityResult = ed.GetNestedEntity(pneo) If pner.Status = PromptStatus.OK Then Try Dim selId As ObjectId = pner.ObjectId ' Let's look at this programmatically-selected ' object, to see what it is Dim obj As DBObject = tr.GetObject(selId, OpenMode.ForRead) ' Check to see if the object is a corridor, if it is not then exit the code. If Not (TypeOf obj Is Corridor) Then Exit Sub End If Dim ent As Entity = DirectCast(obj, Entity) #If C3D2010 Then Dim objCorr As Object = oCivil.AeccDoc.ObjectIdToObject(CType(ent.ObjectId.OldIdPtr, Integer)) #Else Dim objCorr As Object = oCivil.AeccDoc.ObjectIdToObject(CType(ent.ObjectId.OldIdPtr, Long)) #End If Dim oCorridor As AeccCorridor oCorridor = objCorr ' Open the block table record for the current drawing. Dim oBlockTable As BlockTable oBlockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) Dim oBTR As BlockTableRecord oBTR = tr.GetObject(oBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite) ''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Need to add code here to prompt for code and then extract ' the 3DPolylines. Dim sCode As String Dim pr As PromptResult pr = ed.GetString(vbCrLf & "Enter code to extract: ") If pr.Status = PromptStatus.OK Then sCode = pr.StringResult Dim oBaseline As AeccBaseline For Each oBaseline In oCorridor.Baselines Dim oFeatureLines As AeccFeatureLines ' MsgBox(oBaseline.MainBaselineFeatureLines.FeatureLinesCol.Count) For Each oFeatureLines In oBaseline.MainBaselineFeatureLines.FeatureLinesCol Dim oFeatureLine As AeccFeatureLine For Each oFeatureLine In oFeatureLines If String.Compare(oFeatureLine.CodeName, sCode, True) = 0 Then Dim oFeatureLinePts As AeccFeatureLinePoints = oFeatureLine.FeatureLinePoints Dim oFeatureLinePt As AeccFeatureLinePoint Dim oPts As Point3dCollection = New Point3dCollection For Each oFeatureLinePt In oFeatureLinePts ' add the points of the feature line to the collection oPts.Add(New Point3d(oFeatureLinePt.XYZ(0), oFeatureLinePt.XYZ(1), oFeatureLinePt.XYZ(2))) Next ' Create the 3DPolyline Dim oPoly3d As New Polyline3d(New Poly3dType(), oPts, False) oPoly3d.SetDatabaseDefaults() ' Add the new object to the block table record and the transaction oBTR.AppendEntity(oPoly3d) tr.AddNewlyCreatedDBObject(oPoly3d, True) End If Next Next Next Else ed.WriteMessage(vbCrLf & "Incorrect entry") Exit Sub End If Catch tr.Abort() End Try End If End If End If End If tr.Commit() End Using End If Catch ex As Exception End Try End Sub End Class
The source code may be found on this page: http://style.civil3dreminders.com/civil3dreminderspack feel free to download it, update the references, and rebuild using Visual Studio.
The command is improved and in the SincPac where the Civil 3D Reminders went to live in retirement. http://quuxsoft.com/SincpacC3D_Download.aspx
Many thanks Chris,
There are some nice examples in here.
I'm still looking into selecting an individual corridor feature line. I'm still looking into various ways to achieve this. So far, the PointMonitor is able to detect the featurelines under a selected point on a corridor, but I still need a way to drill down into the CorridorFeatureline itself.
The other method I am investigating is using a pickbox selection, drawing a crossing window around the pickbox, and then checking for an intersect of the individual featurelines (extracted as a 3d point collection).
We are able to get to the CorridorFeatureLines from the corridor itself.
Dim cor As Corridor = trans.GetObject(res.ObjectId, OpenMode.ForWrite) 'Get the Corridor from Selection
Dim BaseLines As BaselineCollection = cor.Baselines 'Get the Baselines Collection
Dim Baseline As Baseline = BaseLines(0) 'For my Example I am grabbing the first one.
Dim MainBLFL As BaselineFeatureLines = Baseline.MainBaselineFeatureLines 'Get the MainBaselineFeatureLines
Dim FLColMap As FeatureLineCollectionMap = MainBLFL.FeatureLineCollectionMap 'Get the FeaturelineCollectionMap
Dim fl0 As FeatureLineCollection = FLColMap.Item(0) 'In my example this is Point Code "CL"
Dim fl1 As FeatureLineCollection = FLColMap.Item(1) 'In my example this is Point Code "EOR"
Dim fl_0 As CorridorFeatureLine = fl1(0) 'In my example this is Point Code "EOR" "Right"
Dim fl_1 As CorridorFeatureLine = fl1(1) 'In my example this is Point Code "EOR" "Left"
Dim objId As ObjectId = fl_1.ExportAsGradingFeatureLine(civilDoc.GetSiteIds(0), True) 'Extracted Corridor Featureline
I hope this helps what you were looking for. I was able to extract a feaureline from a selected corridor using the above code. Now the problem is to have the mouse select the appropriate featureline as you stated using PointMonitor or pickbox testing.
So far, the PointMonitor is able to detect the featurelines under a selected point on a corridor
Can you post this code?
I have created and tested the following functions for extracting a CorridorFeatureline from a Corridor.
Public Function ExtractAllFeaturelines(ByVal CorridorObjId As ObjectId, ByVal SiteObjId As ObjectId, ByVal isDynamic As Boolean) As Boolean Dim aDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = aDoc.Editor Dim db As Database = aDoc.Database Dim extracted As Boolean = False Dim civilDoc As CivilDocument = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument
Using trans As Transaction = db.TransactionManager.StartTransaction Try Dim cor As Corridor = trans.GetObject(CorridorObjId, OpenMode.ForRead) For Each Baseline As Baseline In cor.Baselines Dim MainBlFl As BaselineFeatureLines = Baseline.MainBaselineFeatureLines Dim FlColMap As FeatureLineCollectionMap = MainBlFl.FeatureLineCollectionMap For Each FlCol As FeatureLineCollection In FlColMap For Each CorFl As CorridorFeatureLine In FlCol CorFl.ExportAsGradingFeatureLine(SiteObjId, isDynamic) extracted = True Next Next Next trans.Commit() If extracted Then Return True Else Return False End If Catch ex As Autodesk.AutoCAD.Runtime.Exception Return False End Try End Using End Function
Public Function ExtractFeaturelinesByCode(ByVal CorridorObjId As ObjectId, ByVal SiteObjId As ObjectId, ByVal isDynamic As Boolean, ByVal PointCode As String) As Boolean Dim aDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = aDoc.Editor Dim db As Database = aDoc.Database Dim extracted As Boolean = False Dim civilDoc As CivilDocument = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument Using trans As Transaction = db.TransactionManager.StartTransaction Try Dim cor As Corridor = trans.GetObject(CorridorObjId, OpenMode.ForRead) For Each Baseline As Baseline In cor.Baselines Dim MainBlFl As BaselineFeatureLines = Baseline.MainBaselineFeatureLines Dim FlColMap As FeatureLineCollectionMap = MainBlFl.FeatureLineCollectionMap For Each FlCol As FeatureLineCollection In FlColMap For Each CorFl As CorridorFeatureLine In FlCol If CorFl.CodeName = PointCode Then CorFl.ExportAsGradingFeatureLine(SiteObjId, isDynamic) extracted = True End If Next Next Next trans.Commit() If extracted Then Return True Else Return False End If Catch ex As Autodesk.AutoCAD.Runtime.Exception Return False End Try End Using End Function
Thanks for your suggestion Hippe013, I will try that out shortly.
First, with regards to the PointMonitor, I simply lifted code from the Devblog, see link below
This approach looked cool, but would not actually let me select an item, nor did it drill down into the CorridorFeatureLines
In the meantime, I have successfully managed to pick out a CorridorFeaturelLine from a corridor model. I first allow the user to select a corridor item, then draw a pickbox in screen units using the PICKBOX system variable.
I them enumerate through all CorridorFeatureLines, checking each segment against the new pickbox entities. If there is an intersection, I store the CFL into a new list.
This list is then shown in a new dialog, where the user selects the line they want.
I'm still looking into improving the intersection checking (something like line.IntersectsWith())? , but check out the code below and let me know what you think.
[CommandMethod("FLSelect")] public void FLSelect() { // Have the user select a corridor. try { // Get the AutoCAD Editor acApp.Document doc = acApp.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database acCurDb = doc.Database; string sPrompt = "Select corridor:"; PromptEntityOptions entOpts = new PromptEntityOptions(sPrompt); entOpts.Message = sPrompt; PromptEntityResult entRes = null; try { entRes = ed.GetEntity(entOpts); } catch (System.Exception) { ed.WriteMessage("You did not select a valid entity\n"); throw; } if (entRes.Status == PromptStatus.OK) { //Transaction acTrans = acCurDb.TransactionManager.StartTransaction(); FeatureLineInstanceRepository featureLineRepo = new FeatureLineInstanceRepository(); featureLineRepo.ExtractFeatureLines(entRes.ObjectId, acCurDb, ed); using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction()) { // First get the currently selected object Point3d screenPt = entRes.PickedPoint; ObjectId objId = entRes.ObjectId; // Get pickbox's size. Note, the number obtained from // system variable "PICKBOX" is actually the half of // pickbox's width/height object pBox = acApp.Application.GetSystemVariable("PICKBOX"); int pSize = Convert.ToInt32(pBox); int viewPortNumber = Convert.ToInt32(Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("cvport").ToString()); // Define a Point3dCollection for CrossingWindow selecting Point3dCollection points = new Point3dCollection(); System.Windows.Point pScreen; System.Windows.Point p; Point3d pt; pScreen = ed.PointToScreen(screenPt, viewPortNumber); p = new System.Windows.Point(pScreen.X - pSize, pScreen.Y - pSize); pt = ed.PointToWorld(p, viewPortNumber); points.Add(pt); p = new System.Windows.Point(pScreen.X + pSize, pScreen.Y - pSize); pt = ed.PointToWorld(p, viewPortNumber); points.Add(pt); p = new System.Windows.Point(pScreen.X + pSize, pScreen.Y + pSize); pt = ed.PointToWorld(p, viewPortNumber); points.Add(pt); p = new System.Windows.Point(pScreen.X - pSize, pScreen.Y + pSize); pt = ed.PointToWorld(p, viewPortNumber); points.Add(pt); // pickbox cross window lines Line pickBoxLine1 = new Line(new Point3d(points[0].X, points[0].Y, 0.0), new Point3d(points[2].X, points[2].Y, 0.0)); Line pickBoxLine2 = new Line(new Point3d(points[1].X, points[1].Y, 0.0), new Point3d(points[3].X, points[3].Y, 0.0)); // Loop through each item in the feature line list foreach (FeatureLineInstance item in featureLineRepo.FeatureLineRepository) { bool intFound = false; // Loop through each segment of the featureline for (int i = 0; i < item.Points.Count - 1; i++) { Point3d pt1 = new Point3d(item.Points[i].X, item.Points[i].Y, 0.0); Point3d pt2 = new Point3d(item.Points[i + 1].X, item.Points[i + 1].Y, 0.0); Line lineSegment = new Line(pt1, pt2); PointXYZ ptInt = new PointXYZ(); intFound |= DoLinesIntersect(pickBoxLine1, lineSegment, ref ptInt); intFound |= DoLinesIntersect(pickBoxLine2, lineSegment, ref ptInt); if (intFound == true && item.Name != "None") { // Add featureline to FeatureLineUnderPickbox featureLineRepo.FeatureLineUnderPickbox.Add(item); ed.WriteMessage($"Found String: {item.Name}\n"); break; } } } // Add the values to a datagridview if (featureLineRepo.FeatureLineUnderPickbox.Count == 1) { ed.WriteMessage("You have selected: " + featureLineRepo.FeatureLineUnderPickbox[0].Name + "\n"); ed.WriteMessage(" and it contains " + featureLineRepo.FeatureLineUnderPickbox[0].Points.Count.ToString() + " point\n"); } else if (featureLineRepo.FeatureLineUnderPickbox.Count > 1) { // create a new form frmSelectFeatureLine frmSFL = new frmSelectFeatureLine(featureLineRepo.FeatureLineUnderPickbox); acApp.Application.ShowModalDialog(frmSFL); } // You can also use GetPoint and use after Editor.SelectCrossingWindow with a // small window centered on the point to get the entity // (but you are not sure to get a single entity). // To set the size of the window, you can use the PICKBOX system variable. You need to make a conversion pixel->world units. //The safe way is to use GetEntity to get the entity, then GetPoint to get the break point. acTrans.Commit(); } } } catch (System.Exception ex) { } } /// <summary> /// This is based off an explanation and expanded math presented by Paul Bourke: /// /// It takes two lines as inputs and returns true if they intersect, false if they /// don't. /// If they do, ptIntersection returns the point where the two lines intersect. /// </summary> /// <param name="L1">The first line</param> /// <param name="L2">The second line</param> /// <param name="ptIntersection">The point where both lines intersect (if they do).</param> /// <returns></returns> /// http://paulbourke.net/geometry/lineline2d/ /// http://paulbourke.net/geometry/lineline2d/Helpers.cs /// <remarks>See http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/</remarks> public static bool DoLinesIntersect(Line L1, Line L2, ref PointXYZ ptIntersection) { // Denominator for ua and ub are the same, so store this calculation double d = (L2.EndPoint.Y - L2.StartPoint.Y) * (L1.EndPoint.X - L1.StartPoint.X) - (L2.EndPoint.X - L2.StartPoint.X) * (L1.EndPoint.Y - L1.StartPoint.Y); //n_a and n_b are calculated as seperate values for readability double n_a = (L2.EndPoint.X - L2.StartPoint.X) * (L1.StartPoint.Y - L2.StartPoint.Y) - (L2.EndPoint.Y - L2.StartPoint.Y) * (L1.StartPoint.X - L2.StartPoint.X); double n_b = (L1.EndPoint.X - L1.StartPoint.X) * (L1.StartPoint.Y - L2.StartPoint.Y) - (L1.EndPoint.Y - L1.StartPoint.Y ) * (L1.StartPoint.X - L2.StartPoint.X); // Make sure there is not a division by zero - this also indicates that // the lines are parallel. // If n_a and n_b were both equal to zero the lines would be on top of each // other (coincidental). This check is not done because it is not // necessary for this implementation (the parallel check accounts for this). if (d == 0) return false; // Calculate the intermediate fractional point that the lines potentially intersect. double ua = n_a / d; double ub = n_b / d; // The fractional point will be between 0 and 1 inclusive if the lines // intersect. If the fractional calculation is larger than 1 or smaller // than 0 the lines would need to be longer to intersect. if (ua >= 0d && ua <= 1d && ub >= 0d && ub <= 1d) { ptIntersection.X = L1.StartPoint.X + (ua * (L1.EndPoint.X - L1.StartPoint.X)); ptIntersection.Y = L1.StartPoint.Y + (ua * (L1.EndPoint.Y - L1.StartPoint.Y)); return true; } return false; }