In one of my sessions at this year's AU, "There's More to .DWG Than AutoCAD®", I'll be showing some VB.NET code that goes through and collects information about solids, presenting it in a dialog along with the sum of the various volumes. You can get the code and the results from Part 1 of the session's handout.
Just for fun, I thought I'd write some F# code to add the volumes of the 3D solid objects in the modelspace of the current drawing. I adopt a similar approach to the VB code - not caring about intersecting volumes, for instance - but obviously the code looks quite different.
I won't step through the code line-by-line, as the last post introduced the fundamental concepts that also apply here.
// Use lightweight F# syntax
#light
(* Declare a specific namespace
and module name
*)
module MyNamespace.MyApplication
// Import managed assemblies
#I @"C:\Program Files\Autodesk\AutoCAD 2008"
#r "acdbmgd.dll"
#r "acmgd.dll"
open System
open System.Collections.Generic
open Autodesk.AutoCAD.Runtime
open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
// Now we declare our command
[<CommandMethod("volume")>]
let listWords () =
// Let's get the usual helpful AutoCAD objects
let doc =
Application.DocumentManager.MdiActiveDocument;
let ed = doc.Editor;
let db = doc.Database;
// "use" has the same effect as "using" in C#
use tr =
db.TransactionManager.StartTransaction();
// Get appropriately-typed BlockTable and BTRs
let bt =
tr.GetObject
(db.BlockTableId,OpenMode.ForRead)
:?> BlockTable
let ms =
tr.GetObject
(bt.[BlockTableRecord.ModelSpace],
OpenMode.ForRead)
:?> BlockTableRecord
// A function that accepts an ObjectId and returns
// the volume of a 3D solid, if it happens to be one
// Note the valid use of tr, as it is in scope
let getVolume (x : ObjectId) =
let obj = tr.GetObject(x,OpenMode.ForRead);
match obj with
| :? Solid3d -> (obj :?> Solid3d).MassProperties.Volume
| _ -> 0.0
// Use fold_left in a partial application to find
// the sum of the contents of a list
let sum =
List.fold_left (fun x y -> x+y) 0.0
// And here's where we plug everything together...
let vol =
Seq.untyped_to_list ms |> List.map getVolume |> sum
ed.WriteMessage("\nTotal volume: " + vol.ToString());
// As usual, committing is cheaper than aborting
tr.Commit()
The only tricky thing is the use of fold_left to apply an anonymous (or lambda, for the LISPers out there) addition function across the contents of the list containing the individual volumes of the objects in the modelspace.
Here's what we see when we run the "volume" command:
Command: volume
Total volume: 15275.8711619534
This is the same result as displayed by the previous example (although presented with a few more decimal places).