The most difficult thing for us to decided is the level of functionality we provide in the .NET API. We want to provide enough functionality and a flexible design that will allow our users to solve any problems, but we cannot provide solutions to every possible scenario, so we have to strike a balance between most-have vs. nice-to-have functionality.
During the design of the 2012 release, we had long discussions about many features. One of them was the ability to sample elevations on a surface. The COM implementation was crippled in many ways, and we wanted to avoid a similar design in .NET. At the end, it was obvious that we couldn’t provide every single scenario our users will want, but we needed to provide enough functionality to allow them to solve the problem themselves. And that is what we did. The 2012 API contains enough functionality to sample elevations from a Surface yourself. But apparently from the amount of responses I got, this was not enough.
When we planned the 2013 release, we revisit our discussions on how to provide the functionality our users wanted. Back in the COM days, we had many requests to provide functionality, not only to sample elevations between two points, but to also allow other type of entities like arcs and polylines. This is understandable because if you wanted, among other things, to create your own profile, you will need that functionality, so it seems reasonable that the API allows you to do it.
Of course, you cannot make everyone happy, so some of the complaints now are that sampling elevations between two points is more complicated in .NET than it used to be in COM. I agree, but let me explain why.
Again, this is a balancing exercise between complexity and flexibility. Initially, it may seem the .NET implementation is more complex, but this is just the case when you want to sample elevations between two points. If you were to try sampling elevations along a polyline in COM, you will immediately realize that there are other levels of complexity that you need to solve. These problems are already solved inside of Civil 3D behind the scenes, and it would not make sense that you will have to solve them again yourself.
The .NET API provides functionality to sample elevations along any type of Curve object, this includes Lines, Arcs, and Polylines, but it will also work with any other type of object derived from Curve. This solution provides all the flexibility you will need in any kind of situation and will allow you to solve any kind of problem requiring elevation samples.
Still, some of you will like to be able to sample elevations between two points. In this case, I recommend you review the implementation Partha Sarkar provided in the Infrastructure Modeling Blog. He demonstrates how adapt the current design to make it work the way the COM API used to, by creating a temporary polyline and use it to sample the elevations. This is a good technique if all you want is the good old COM functionality. If you need more flexibility, here is a command that will sample elevations from any kind of Curve derived object. Since the result of sampling elevations is a collection of Point3d objects, I use sampling points as the term instead of sampling elevations.
C#
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Civil.DatabaseServices;
using CivilSurface = Autodesk.Civil.DatabaseServices.Surface;
[assembly: CommandClass(
typeof(Autodesk.CivilizedDevelopment.SamplingPointsCommands))]
namespace Autodesk.CivilizedDevelopment
{
public class SamplingPointsCommands : SimpleDrawingCommand
{
[CommandMethod("CDS_SamplePointsOnSurface")]
public void CDS_SamplePointsOnSurface()
{
using (Transaction tr = startTransaction())
{
ITerrainSurface surface = getSurface() as ITerrainSurface;
if (surface == null) return;
ObjectId curveId = getCurveId();
displayPoints(surface.SampleElevations(curveId));
}
}
private CivilSurface getSurface()
{
ObjectId surfaceId = promptForSurface();
if (surfaceId.IsNull)
{
return null;
}
CivilSurface surface = surfaceId.GetObject(OpenMode.ForRead)
as CivilSurface;
return surface;
}
private ObjectId promptForSurface()
{
PromptEntityOptions options = new PromptEntityOptions(
"\nSelect a TIN Surface: ");
options.SetRejectMessage(
"\nThe selected object is not a TIN Surface.");
options.AddAllowedClass(typeof(CivilSurface), false);
PromptEntityResult result = _editor.GetEntity(options);
if (result.Status == PromptStatus.OK)
{
// Everything is cool; we return the selected
// surface ObjectId.
return result.ObjectId;
}
return ObjectId.Null; // Indicating error.
}
private ObjectId getCurveId()
{
PromptEntityOptions options = new PromptEntityOptions(
"\nSelect entity: ");
options.SetRejectMessage(
"\nThe selected entity is not of the valid type.");
PromptEntityResult result = _editor.GetEntity(options);
if (result.Status == PromptStatus.OK)
{
return result.ObjectId;
}
return ObjectId.Null;
}
private void displayPoints(Point3dCollection points)
{
foreach (Point3d point in points)
{
_editor.WriteMessage("\n" + point.ToString());
}
}
}
}
VB.NET
Imports System
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.Civil.DatabaseServices
Imports CivilSurface = Autodesk.Civil.DatabaseServices.Surface
<Assembly: CommandClass(
GetType(Autodesk.CivilizedDevelopment.SamplingPointsCommands))>
Namespace Autodesk.CivilizedDevelopment
Public Class SamplingPointsCommands
Inherits SimpleDrawingCommand
<CommandMethod("CDS_SamplePointsOnSurface")> _
Public Sub CDS_SamplePointsOnSurface()
Using tr As Transaction = startTransaction()
Dim surface As ITerrainSurface = TryCast(getSurface(),
ITerrainSurface)
If surface Is Nothing Then
Return
End If
Dim curveId As ObjectId = getCurveId()
displayPoints(surface.SampleElevations(curveId))
End Using
End Sub
Private Function getSurface() As CivilSurface
Dim surfaceId As ObjectId = promptForSurface()
If surfaceId.IsNull Then
Return Nothing
End If
Dim surface As CivilSurface =
TryCast(surfaceId.GetObject(OpenMode.ForRead), CivilSurface)
Return surface
End Function
Private Function promptForSurface() As ObjectId
Dim options As New PromptEntityOptions(
vbLf & "Select a TIN Surface: ")
options.SetRejectMessage(
vbLf & "The selected object is not a TIN Surface.")
options.AddAllowedClass(GetType(CivilSurface), False)
Dim result As PromptEntityResult = _editor.GetEntity(options)
If result.Status = PromptStatus.OK Then
' Everything is cool; we return the selected
' surface ObjectId.
Return result.ObjectId
End If
Return ObjectId.Null
' Indicating error.
End Function
Private Function getCurveId() As ObjectId
Dim options As New PromptEntityOptions(vbLf & "Select entity: ")
options.SetRejectMessage(
vbLf & "The selected entity is not of the valid type.")
Dim result As PromptEntityResult = _editor.GetEntity(options)
If result.Status = PromptStatus.OK Then
Return result.ObjectId
End If
Return ObjectId.Null
End Function
Private Sub displayPoints(points As Point3dCollection)
For Each point As Point3d In points
_editor.WriteMessage(vbLf + point.ToString())
Next
End Sub
End Class
End Namespace
When we design the .NET API, we have to balance the functionality that we include to keep it flexible and to remove complexity. Often times, we will find functionality that it is not included but it is widely used and desired. In those cases, we will revisit our decisions and try to provide the best solution possible. This is why your feedback is very important, and your complaints necessary. I am sure we have missed things in the new 2013 API, so if you let us know, we are listening.