One of the frequently asked questions to our technical support is “How to calculate the sum of line lengths (pipeline sections, elements of wire diagram etc.) in a drawing?” There are many ways to solve this problem, and today we will consider an implementation of the MultiCAD.NET application that sums line lengths in nanoCAD, AutoCAD and ZWCAD. As an example we will calculate the sum of pipe lengths in the water supply scheme with two options to select elements for calculating: user selection and selection by the object filter.
Calculating the length of lines selected by user
Before we set to work, we need to know what a line in MultiCAD.NET is. The line is a standard primitive like circle, text, spline etc. To represent a line within the drawing database, the DbLine
class from the Multicad.DatabaseServices.StandardObjects
namespace is used.
A DbLine
object has properties that store the first and second points of the line but does not contain the length information. Surely, we could calculate the length by the points coordinates, however it is more efficient to access the line’s geometric representation. The geometry data is stored in the LineSeg3d class object that can be accessed through the DbLine.Line
property and has the Length property to get the length:
1 2 3 |
double length = line.Line.Length; |
Now let’s consider the first option when the user selects a number of lines for calculating the total length. To provide the user with an ability to select objects the SelectObjects
method of the McObjectManager
class is used:
1 2 3 |
public static McObjectId[] SelectObjects(string sPromt); |
The method prompts the user to select entities on the drawing and writes the IDs of selected objects into the array. But we need to make sure the user has selected only entities of desired type. That is why we filter out only IDs that belong to DbLine
entities from the set, then get length of each of them and increment the result. Below is the command that implements this procedure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
CommandMethod("LineLengthSum", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void LineLengthSum() { // Gets selected objects McObjectId[] idSelecteds = McObjectManager.SelectObjects("Выберите объекты типа линия"); if (idSelecteds.Length == 0) { MessageBox.Show("No objects selected"); return; } double lengthSum = 0; foreach (McObjectId currID in idSelecteds) { McObject currObj = currID.GetObject(); // If the object is DbLine, increments the length if (currObj is DbLine) { lengthSum += (currObj as DbLine).Line.Length; } } MessageBox.Show(lengthSum.ToString(), "The total length", MessageBoxButtons.OK, MessageBoxIcon.Information); } |
The same approach works also for polylines that consists of one or more line (or arc) segments. The length of a polyline can be derived similarly to a line by using the geometrical representation class Polyline3d
that can be accessed for every DbPolyline
object through the Polyline
property:
1 2 3 |
double length = polyline.Polyline.Length; |
Automatic calculation of the total lines length
When the drawing contains many elements, it makes sense to select object automatically to avoid user input errors. For this purpose an object filter is used. It selects objects that obey the filter criteria: search area (sheets, layers, documents, regions) and object types.
For example, to filter all lines on the specific layer, the filter with specified layer name is used:
1 2 3 4 5 6 |
ObjectFilter filter = ObjectFilter.Create(true); filter.AddLayer("Cold water"); filter.AddType(typeof(DbLine)); List ids = filter.GetObjects(); |
In practice automatic line length calculation is applied commonly for creating a report with a list of pipe types on a water supply scheme. Here is the example scheme:
In our example there are two types of water pipes: of PVC and stainless steel. Each type is drawn with lines or polylines on a separate layer: Circuit1 or Circuit2.
The command below creates a text report with listing layer names and total pipe lengths for each of them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[CommandMethod("createReport", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void createReport() { List reportStrings = getLengthSumByLayer(); if (reportStrings.Count == 0) { MessageBox.Show("The drawing doesn’t contain lines or polylines"); return; } // Text indent int indent = 0; // Report header DbText caption = new DbText(); caption.Text = new TextGeom("Pipe lengths by type:", new Point3d(0, indent, 0), Vector3d.XAxis, "Standard", 10); caption.DbEntity.AddToCurrentDocument(); foreach (String str in reportStrings) { indent -= 10; DbText reportText = new DbText(); reportText.Text = new TextGeom(str, new Point3d(0, indent, 0), Vector3d.XAxis, "Standard", 6); reportText.DbEntity.AddToCurrentDocument(); } } |
Calculation of the total line lengths and filling up the report are performed in the getLengthSumByLayer()
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public List getLengthSumByLayer() { List reportStrings = new List(); // Run through all layers, find lines and polylines on each layer and // calculates the total length List layers = McObjectManager.CurrentStyle.GetLayers(); foreach (string layerName in layers) { ObjectFilter filter = ObjectFilter.Create(true).AddType(typeof(DbLine)).AddType(typeof(DbPolyline)).AddLayer(layerName); List idSelected = filter.GetObjects(); if (idSelected.Count != 0) { double lengthSum = 0; foreach (McObjectId currID in idSelected) { // Gets an object by ID McObject currObj = currID.GetObject(); // If the object is line or polyline, increments the length if (currObj is DbLine) { lengthSum += (currObj as DbLine).Line.Length; } else if (currObj is DbPolyline) { lengthSum += (currObj as DbPolyline).Polyline.Length; } } // If the total length of lines or polylines on the layer is non-nil, then add a text to the report if (lengthSum != 0) { reportStrings.Add(layerName.ToString() + ": " + lengthSum.ToString()); } } } return reportStrings; } |
As the result of command execution the following report will be added on the drawing:
The step-by-step guidance of how to load MultiCAD.NET applications you can find in the post MultiCAD Enabler for AutoCAD and ZWCAD.