最近有个工程预算方面的项目,需要获取到两个实体搭接部分的体积,通过网络查找和SDK 文档与Demo的阅读,总结出以下解决方案,有需要的同学可以参考下,开发的SDK版本为2013。
namespace VolumnOfJoint
{
[Transaction(TransactionMode.Manual)]
class CsCompute : IExternalCommand
{
Application app = null;
Document doc = null;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
doc = commandData.Application.ActiveUIDocument.Document;
app = commandData.Application.Application;
Parameter paramVolume = null;
Transaction transaction = new Transaction(doc);
transaction.Start("Compute volume of joint");
GetSharedParamters(ref paramVolume);
//VolumeCalculationOptions options = doc.Settings.VolumeCalculationSetting.VolumeCalculationOptions;
//options.VolumeComputationEnable = true;
//doc.Settings.VolumeCalculationSetting.VolumeCalculationOptions = options;
UIDocument curDoc = commandData.Application.ActiveUIDocument;
IList<Reference> elemRefSet = null;
try
{
elemRefSet = curDoc.Selection.PickObjects(ObjectType.Element, "Please select two elemets");
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
{
transaction.RollBack();
return Result.Succeeded;
}
int elemSelCount = elemRefSet.Count;
if (elemSelCount != 2)
{
TaskDialog.Show("Warning", "Please select two elemets!");
}
else
{
Element elem1th = doc.GetElement(elemRefSet[0]);
Element elem2th = doc.GetElement(elemRefSet[1]);
Solid soid1th = GetSolid(elem1th);
Solid soid2th = GetSolid(elem2th);
string computeInfo = "";
bool computeSuccess = false;
double volumnJoint = 0.0f;
if (soid1th != null && soid2th != null)
{
Solid soidJoint = BooleanOperationsUtils.ExecuteBooleanOperation(soid1th, soid2th, BooleanOperationsType.Intersect);
if (soidJoint != null)
{
volumnJoint = soidJoint.Volume;
computeSuccess = true;
}
}
if (computeSuccess)
{
computeInfo = "Element:" + elem1th.Name + "\r\n";
/*
computeInfo += "Surface Area:" + soid1th.SurfaceArea + "\r\n";
computeInfo += "Volume:" + soid1th.Volume + "\r\n";
*/
paramVolume.Set(soid1th.Volume);
computeInfo += "Volume:" + paramVolume.AsValueString() + "\r\n" + "\r\n";
computeInfo += "Element:" + elem2th.Name + "\r\n";
/*
computeInfo += "Surface Area:" + soid2th.SurfaceArea + "\r\n";
computeInfo += "Volume:" + soid2th.Volume + "\r\n";
*/
paramVolume.Set(soid2th.Volume);
computeInfo += "Volume:" + paramVolume.AsValueString() + "\r\n" + "\r\n";
// computeInfo += "Volume of Joint:" + volumnJoint + "\r\n";
paramVolume.Set(volumnJoint);
computeInfo += "Volume of Joint:" + paramVolume.AsValueString();
TaskDialog.Show("Compute Success", computeInfo);
}
else
{
TaskDialog.Show("Compute failed", "Can not get the volume of joint!");
}
}
transaction.RollBack();
return Result.Succeeded;
}
public Solid GetSolid(Element elem)
{
Options opt = new Options();
opt.ComputeReferences = true;
opt.IncludeNonVisibleObjects = false;
GeometryElement geoElem = elem.get_Geometry(opt);
foreach (GeometryObject geomObj in geoElem)
{
if (geomObj is Solid)
{
Solid solidTmp = geomObj as Solid;
if (solidTmp != null && solidTmp.Faces.Size > 0)
{
return solidTmp;
}
}
else if (geomObj is GeometryInstance)
{
GeometryInstance geoInst = geomObj as GeometryInstance;
GeometryElement geoElemTmp = geoInst.GetInstanceGeometry();
foreach (GeometryObject geomObjTmp in geoElemTmp)
{
Solid solidTmp = geomObjTmp as Solid;
if (solidTmp != null && solidTmp.Faces.Size > 0)
{
return solidTmp;
}
}
}
}
return null;
}
public void GetSharedParamters(ref Parameter paramVolumn)
{
AddSharedParamters();
Element elemProjectInfo = GetProjectInfoElem();
paramVolumn = elemProjectInfo.get_Parameter("myVolume");
}
public void AddSharedParamters()
{
string sharedParameterFile = "c:\\sharedParamter.txt";
FileStream fileStream = File.Create(sharedParameterFile);
fileStream.Close();
app.SharedParametersFilename = sharedParameterFile;
DefinitionFile fileDef = app.OpenSharedParameterFile();
DefinitionGroups myGroups = fileDef.Groups;
DefinitionGroup myGroup = myGroups.Create("MyParameters");
Definition myDefinition_Volume = myGroup.Definitions.Create("myVolume", ParameterType.Volume, true);
// Definition myDefinition_SurfaceArea = myGroup.Definitions.Create("mySurfaceArea", ParameterType.SurfaceArea, true); ParameterType.SurfaceArea seems dosen't supported
CategorySet myCategories = app.Create.NewCategorySet();
Category myCategory = doc.Settings.Categories.get_Item(BuiltInCategory.OST_ProjectInformation);
bool visible = myCategory.AllowsBoundParameters;
myCategories.Insert(myCategory);
//Create an object of InstanceBinding according to the Categories
InstanceBinding instanceBinding = app.Create.NewInstanceBinding(myCategories);
BindingMap bindingMap = doc.ParameterBindings;
// Bind the definitions to the document
bindingMap.Insert(myDefinition_Volume, instanceBinding);
// bindingMap.Insert(myDefinition_SurfaceArea, instanceBinding);
}
public Element GetProjectInfoElem()
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfCategory(BuiltInCategory.OST_ProjectInformation);
IList<Element> elems = collector.ToElements();
return elems[0];
}
}
}
这里需要注意下,一定要先将需要检测的两个实体转换到世界坐标空间下再调用接口进行搭接部分体积计算,不然这两个实体仅仅是Symbol,不可能有交集的,返回的交接部分实体的体积自然为0,即对于GeometryInstance一定要调用GetInstanceGeometry()而不是GetSymbolGeometry(), 还有一点需要注意的是单位问题,Revit内部是以固定的单位做实际的数据存储和运算而并不是以用户在项目单位中所设置的单位,那么在实际存储数据与上层项目表现和操作数据之间就会出现单位转换的问题,经过测试发现这里的计算出的体积单位是立方英尺, 可以手动强制转换,我这里是使用共享参数的方式让Revit自行换算出最终符合当前项目单位设置的结果,这种方式应该最合适的。
可供参考资料:
http://en.wikipedia.org/wiki/Constructive_solid_geometry
SDK Samples\GeometryAPI\GeometryCreation_BooleanOperation