应用程序名称: FreeFormElement
Revit 平台: 所有版本
Revit 版本: 2014.0
首次发布版本: 2014.0
编程语言: C#
技能等级: 中等
类别: 几何图形
类型: 外部应用程序
主题: 创建FreeFormElement
摘要:
使用几何工具创建一个新的族,该族从现有元素中削减了一部分。
相关类:
Autodesk.Revit.UI.IExternalCommand
Autodesk.Revit.DB.FreeFormElement
Autodesk.Revit.DB.BooleanOperationsUtils
Autodesk.Revit.DB.CurveLoop
Autodesk.Revit.DB.Curve
项目文件:
- Application.cs - 实现了Revit插件IExternalApplication。
- CreateNegativeBlockCommand.cs - 一个命令,用于创建一个新的族块,表示所选元素的负块。
- FreeFormElementUtils.cs - 支持创建一个包含FreeFormElement的族的工具,该元素从现有的几何图形中被削减。
描述:
本示例:
- 提示用户在项目中选择一个实体元素作为模板。
- 提示用户选择一个平行于XY平面的闭合曲线环。
- 创建通用模型族。
- 在该族中创建一个FreeFormElement。几何图形将包括:
- 将闭合曲线环向上挤压到目标高度以上
- 从挤出的块中减去目标的几何图形
- 将该族加载到项目中,并在原始曲线上放置一个实例。
说明:
1.打开包含在内的Revit项目,或其他已设置适当元素的项目。
2.选择"Add-ins" -> "FreeForm" -> "Create negative block"按钮。
3.选择目标实体元素。
4.选择曲线环(所有曲线必须逐个选择或通过框选选择)。
5.选择"完成"。
新的族应该已经被创建、加载并放置在模型中。
源代码:
完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码
Application.cs
//
// (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved.
//
// 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 ITS 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.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Autodesk;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using System.Windows.Media.Imaging;
using System.Windows;
namespace Revit.SDK.Samples.FreeFormElement.CS
{
/// <summary>
/// Implements the Revit add-in interface IExternalApplication
/// </summary>
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class Application : IExternalApplication
{
#region IExternalApplication Members
/// <summary>
/// Implements the OnShutdown event
/// </summary>
/// <param name="application"></param>
/// <returns></returns>
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
/// <summary>
/// Implements the OnStartup event
/// </summary>
/// <param name="application"></param>
/// <returns></returns>
public Result OnStartup(UIControlledApplication application)
{
CreateFreeformElementPanel(application);
return Result.Succeeded;
}
#endregion
/// <summary>
/// Creates the ribbon panel for the sample.
/// </summary>
/// <param name="application">The application.</param>
private void CreateFreeformElementPanel(UIControlledApplication application)
{
RibbonPanel rp = application.CreateRibbonPanel("FreeForm");
PushButtonData freeform = new PushButtonData("Negative_block", "Create negative block",
addAssemblyPath,
typeof(Revit.SDK.Samples.FreeFormElement.CS.CreateNegativeBlockCommand).FullName);
PushButton freeformPB = rp.AddItem(freeform) as PushButton;
SetIconsForPushButton(freeformPB, Revit.SDK.Samples.FreeFormElement.CS.Properties.Resources.CreateNegative);
}
/// <summary>
/// Utility for adding icons to the button.
/// </summary>
/// <param name="button">The push button.</param>
/// <param name="icon">The icon.</param>
private static void SetIconsForPushButton(PushButton button, System.Drawing.Icon icon)
{
button.LargeImage = GetStdIcon(icon);
button.Image = GetSmallIcon(icon);
}
/// <summary>
/// Gets the standard sized icon as a BitmapSource.
/// </summary>
/// <param name="icon">The icon.</param>
/// <returns>The BitmapSource.</returns>
private static BitmapSource GetStdIcon(System.Drawing.Icon icon)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
/// <summary>
/// Gets the small sized icon as a BitmapSource.
/// </summary>
/// <param name="icon">The icon.</param>
/// <returns>The BitmapSource.</returns>
private static BitmapSource GetSmallIcon(System.Drawing.Icon icon)
{
System.Drawing.Icon smallIcon = new System.Drawing.Icon(icon, new System.Drawing.Size(16, 16));
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
smallIcon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
/// <summary>
/// The path to this add-in assembly.
/// </summary>
static String addAssemblyPath = typeof(Revit.SDK.Samples.FreeFormElement.CS.Application).Assembly.Location;
}
}
CreateNegativeBlockCommand.cs
//
// (C) Copyright 2003-2019 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.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB;
namespace Revit.SDK.Samples.FreeFormElement.CS
{
/// <summary>
/// A command to create a new family block representing a negative of a selected element.
/// </summary>
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
class CreateNegativeBlockCommand : IExternalCommand
{
#region IExternalCommand Members
public Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
{
UIDocument uiDoc = commandData.Application.ActiveUIDocument;
Document doc = uiDoc.Document;
// Select target element
Reference target = uiDoc.Selection.PickObject(ObjectType.Element,
new TargetElementSelectionFilter(),
"Select target");
Element targetElement = doc.GetElement(target);
// Get height for block based on target element.
BoundingBoxXYZ bbox = targetElement.get_BoundingBox(null);
double height = bbox.Max.Z - bbox.Min.Z + 1;
// Select boundaries
IList<Reference> boundaries = uiDoc.Selection.PickObjects(ObjectType.Element,
new BoundarySelectionFilter(),
"Select boundary");
String familyPath = FreeFormElementUtils.FindGenericModelTemplate(doc.Application.FamilyTemplatePath);
if (String.IsNullOrEmpty(familyPath))
{
message = "Unable to find a template named 'GenericModel.rft' in family template path.";
return Result.Failed;
}
FreeFormElementUtils.FailureCondition condition = FreeFormElementUtils.CreateNegativeBlock(targetElement, boundaries, UIDocument.GetRevitUIFamilyLoadOptions(), familyPath);
// Show error message for failure condition
if (condition != FreeFormElementUtils.FailureCondition.Success)
{
switch (condition)
{
case FreeFormElementUtils.FailureCondition.CurvesNotContigous:
message = "Could not create the block as the boundary curves do not make a contiguous closed boundary.";
break;
case FreeFormElementUtils.FailureCondition.CurveLoopAboveTarget:
message = "Could not create the block as the boundary curves lie above their target element.";
break;
case FreeFormElementUtils.FailureCondition.NoIntersection:
message = "Could not create the block as the curves and the target element does not intersect.";
break;
}
return Result.Failed;
}
return Result.Succeeded;
}
#endregion
}
/// <summary>
/// Selection filter for selection of a target object to use as a template for the negative block.
/// </summary>
class TargetElementSelectionFilter : ISelectionFilter
{
public bool AllowElement(Element element)
{
// Element must have at least one usable solid
IList<Solid> solids = FreeFormElementUtils.GetTargetSolids(element);
return solids.Count > 0;
}
public bool AllowReference(Reference refer, XYZ point)
{
return true;
}
}
/// <summary>
/// Selection filter for selection of the boundary curves for the block extents.
/// </summary>
class BoundarySelectionFilter : ISelectionFilter
{
public bool AllowElement(Element element)
{
// Allow only curve elements
CurveElement curveElement = element as CurveElement;
if (curveElement == null)
return false;
Curve curve = curveElement.GeometryCurve;
// Curves must support the utilities used by the tool (e.g. ReverseCurve)
if (!FreeFormElementUtils.SupportsLoopUtilities(curve))
return false;
// Curves must be in XY plane
return FreeFormElementUtils.IsCurveInXYPlane(curve);
}
public bool AllowReference(Reference refer, XYZ point)
{
return true;
}
}
}
FreeFormElementUtils.cs
//
// (C) Copyright 2003-2019 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.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Autodesk.Revit.DB;
using RevitFreeFormElement = Autodesk.Revit.DB.FreeFormElement;
namespace Revit.SDK.Samples.FreeFormElement.CS
{
/// <summary>
/// Utilities supporting the creation of a family containing a FreeFormElement which is cut out from existing geometry
/// </summary>
class FreeFormElementUtils
{
public enum FailureCondition
{
Success,
CurvesNotContigous,
CurveLoopAboveTarget,
NoIntersection
};
/// <summary>
/// Creates a negative block family from the geometry of the target element and boundaries.
/// </summary>
/// <remarks>This is the main implementation of the sample command.</remarks>
/// <param name="targetElement">The target solid element.</param>
/// <param name="boundaries">The selected curve element boundaries.</param>
/// <param name="familyLoadOptions">The family load options when loading the new family.</param>
/// <param name="familyTemplate">The family template.</param>
public static FailureCondition CreateNegativeBlock(Element targetElement, IList<Reference> boundaries, IFamilyLoadOptions familyLoadOptions, String familyTemplate)
{
Document doc = targetElement.Document;
Autodesk.Revit.ApplicationServices.Application app = doc.Application;
// Get curve loop for boundary
IList<Curve> curves = GetContiguousCurvesFromSelectedCurveElements(doc, boundaries);
CurveLoop loop = null;
try
{
loop = CurveLoop.Create(curves);
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
// Curves are not contiguous
return FailureCondition.CurvesNotContigous;
}
List<CurveLoop> loops = new List<CurveLoop>();
loops.Add(loop);
// Get elevation of loop
double elevation = curves[0].GetEndPoint(0).Z;
// Get height for extrusion
BoundingBoxXYZ bbox = targetElement.get_BoundingBox(null);
double height = bbox.Max.Z - elevation;
if (height <= 1e-5)
return FailureCondition.CurveLoopAboveTarget;
height += 1;
// Create family
Document familyDoc = app.NewFamilyDocument(familyTemplate);
// Create block from boundaries
Solid block = GeometryCreationUtilities.CreateExtrusionGeometry(loops, XYZ.BasisZ, height);
// Subtract target element
IList<Solid> fromElement = GetTargetSolids(targetElement);
int solidCount = fromElement.Count;
// Merge all found solids into single one
Solid toSubtract = null;
if (solidCount == 1)
{
toSubtract = fromElement[0];
}
else if (solidCount > 1)
{
toSubtract =
BooleanOperationsUtils.ExecuteBooleanOperation(fromElement[0], fromElement[1], BooleanOperationsType.Union);
}
if (solidCount > 2)
{
for (int i = 2; i < solidCount; i++)
{
toSubtract = BooleanOperationsUtils.ExecuteBooleanOperation(toSubtract, fromElement[i],
BooleanOperationsType.Union);
}
}
// Subtract merged solid from overall block
try
{
BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(block, toSubtract,
BooleanOperationsType.Difference);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
return FailureCondition.NoIntersection;
}
// Create freeform element
using (Transaction t = new Transaction(familyDoc, "Add element"))
{
t.Start();
RevitFreeFormElement element = Autodesk.Revit.DB.FreeFormElement.Create(familyDoc, block);
t.Commit();
}
// Load family into document
Family family = familyDoc.LoadFamily(doc, familyLoadOptions);
familyDoc.Close(false);
// Get symbol as first symbol of loaded family
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.WherePasses(new FamilySymbolFilter(family.Id));
FamilySymbol fs = collector.FirstElement() as FamilySymbol;
// Place instance at location of original curves
using (Transaction t2 = new Transaction(doc, "Place instance"))
{
t2.Start();
if (!fs.IsActive)
fs.Activate();
doc.Create.NewFamilyInstance(XYZ.Zero, fs, Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
t2.Commit();
}
return FailureCondition.Success;
}
/// <summary>
/// Gets a list of curves which are ordered correctly and oriented correctly to form a closed loop.
/// </summary>
/// <param name="doc">The document.</param>
/// <param name="boundaries">The list of curve element references which are the boundaries.</param>
/// <returns>The list of curves.</returns>
public static IList<Curve> GetContiguousCurvesFromSelectedCurveElements(Document doc, IList<Reference> boundaries)
{
List<Curve> curves = new List<Curve>();
// Build a list of curves from the curve elements
foreach (Reference reference in boundaries)
{
CurveElement curveElement = doc.GetElement(reference) as CurveElement;
curves.Add(curveElement.GeometryCurve.Clone());
}
// Walk through each curve (after the first) to match up the curves in order
for (int i = 0; i < curves.Count; i++)
{
Curve curve = curves[i];
XYZ endPoint = curve.GetEndPoint(1);
// find curve with start point = end point
for (int j = i + 1; j < curves.Count; j++)
{
// Is there a match end->start, if so this is the next curve
if (curves[j].GetEndPoint(0).IsAlmostEqualTo(endPoint, 1e-05))
{
Curve tmpCurve = curves[i + 1];
curves[i + 1] = curves[j];
curves[j] = tmpCurve;
continue;
}
// Is there a match end->end, if so, reverse the next curve
else if (curves[j].GetEndPoint(1).IsAlmostEqualTo(endPoint, 1e-05))
{
Curve tmpCurve = curves[i + 1];
curves[i + 1] = CreateReversedCurve(curves[j]);
curves[j] = tmpCurve;
continue;
}
}
}
return curves;
}
/// <summary>
/// Utility to create a new curve with the same geometry but in the reverse direction.
/// </summary>
/// <param name="orig">The original curve.</param>
/// <returns>The reversed curve.</returns>
/// <throws cref="NotImplementedException">If the curve type is not supported by this utility.</throws>
private static Curve CreateReversedCurve(Curve orig)
{
if (!SupportsLoopUtilities(orig))
{
throw new NotImplementedException("CreateReversedCurve for type " + orig.GetType().Name);
}
if (orig is Line)
{
return Line.CreateBound(orig.GetEndPoint(1), orig.GetEndPoint(0));
}
else if (orig is Arc)
{
return Arc.Create(orig.GetEndPoint(1), orig.GetEndPoint(0), orig.Evaluate(0.5, true));
}
else
{
throw new Exception("CreateReversedCurve - Unreachable");
}
}
/// <summary>
/// Identifies if the curve type is supported in these utilities.
/// </summary>
/// <param name="curve">The curve.</param>
/// <returns>True if the curve type is supported, false otherwise.</returns>
public static bool SupportsLoopUtilities(Curve curve)
{
return curve is Line || curve is Arc;
}
/// <summary>
/// Identifies if the curve lies entirely in an XY plane (Z = constant)
/// </summary>
/// <param name="curve">The curve.</param>
/// <returns>True if the curve lies in an XY plane, false otherwise.</returns>
public static bool IsCurveInXYPlane(Curve curve)
{
// quick reject - are endpoints at same Z
double zDelta = curve.GetEndPoint(1).Z - curve.GetEndPoint(0).Z;
if (Math.Abs(zDelta) > 1e-05)
return false;
if (!(curve is Line) && !curve.IsCyclic)
{
// Create curve loop from curve and connecting line to get plane
List<Curve> curves = new List<Curve>();
curves.Add(curve);
curves.Add(Line.CreateBound(curve.GetEndPoint(1), curve.GetEndPoint(0)));
CurveLoop curveLoop = CurveLoop.Create(curves);
XYZ normal = curveLoop.GetPlane().Normal.Normalize();
if (!normal.IsAlmostEqualTo(XYZ.BasisZ) && !normal.IsAlmostEqualTo(XYZ.BasisZ.Negate()))
return false;
}
return true;
}
/// <summary>
/// Gets all target solids in a given element.
/// </summary>
/// <remarks>Includes solids and solids in first level instances only. Deeper levels are ignored. Empty solids are not returned.</remarks>
/// <param name="element">The element.</param>
/// <returns>The list of solids.</returns>
public static IList<Solid> GetTargetSolids(Element element)
{
List<Solid> solids = new List<Solid>();
Options options = new Options();
options.DetailLevel = ViewDetailLevel.Fine;
GeometryElement geomElem = element.get_Geometry(options);
foreach (GeometryObject geomObj in geomElem)
{
if (geomObj is Solid)
{
Solid solid = (Solid)geomObj;
if (solid.Faces.Size > 0 && solid.Volume > 0.0)
{
solids.Add(solid);
}
// Single-level recursive check of instances. If viable solids are more than
// one level deep, this example ignores them.
}
else if (geomObj is GeometryInstance)
{
GeometryInstance geomInst = (GeometryInstance)geomObj;
GeometryElement instGeomElem = geomInst.GetInstanceGeometry();
foreach (GeometryObject instGeomObj in instGeomElem)
{
if (instGeomObj is Solid)
{
Solid solid = (Solid)instGeomObj;
if (solid.Faces.Size > 0 && solid.Volume > 0.0)
{
solids.Add(solid);
}
}
}
}
}
return solids;
}
/// <summary>
/// Finds the Generic Model template from the family template directory path, if it exists.
/// </summary>
/// <param name="familyPath">The family template directory path.</param>
/// <returns>The template path, or empty string if not found.</returns>
public static String FindGenericModelTemplate(String familyPath)
{
string hardCodedResult = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(CreateNegativeBlockCommand).Assembly.Location), "Generic Model.rft");
try
{
IEnumerable<string> files = Directory.EnumerateFiles(familyPath, "Generic Model.rft", SearchOption.AllDirectories);
string result = files.FirstOrDefault<string>();
if (!String.IsNullOrEmpty(result))
return result;
files = Directory.EnumerateFiles(familyPath, "Metric Generic Model.rft", SearchOption.AllDirectories);
result = files.FirstOrDefault<string>();
if (!String.IsNullOrEmpty(result))
return result;
return hardCodedResult;
}
catch (Exception)
{
return hardCodedResult;
}
}
}
}
版权所有 :无锡模信建筑科技有限公司 苏ICP备2021028830号-1 BIM建模|BIM技术应用|BIM软件开发
联系地址:江苏省无锡市新吴区龙山路4号B座705 手机:18761516598