应用程序:MultithreadedCalculation
Revit平台:所有
Revit版本:2012.0
首次发布:2012.0
编程语言:C#
技能水平:高级
类别:几何
类型:ExternalCommand
主题:分析可视化框架
摘要:通过分析可视化框架、多线程、Revit的空闲事件和动态模型更新的组合,模拟分析结果在计算过程中如何周期性地更新,并在Revit模型发生更改时重新启动。
类:
Autodesk.Revit.DB.Analysis.FieldDomainPointsByUV
Autodesk.Revit.DB.Analysis.FieldValues
Autodesk.Revit.DB.Analysis.SpatialFieldManager
Autodesk.Revit.DB.UpdaterRegistry
System.Threading.Thread
项目文件:
MultithreadedCalculation.cs
描述:
1.提示用户选择一个元素,使用Selection.PickObject。
2.找到元素的最大面。
3.获取活动视图的SpatialFieldManager,如果活动视图没有,则创建一个SpatialFieldManager。
4.将元素最大面的引用添加到SpatialFieldManager中。
5.查找面的边界框的最小和最大UV值
使用myUV类是因为它可以安全地传递到外部线程。myUV包含两个double值。本机的Revit类(如Autodesk.Revit.DB.UV)无法传递到线程。
6.创建并注册IUpdater(SpatialFieldUpdater),以便在更改或删除步骤1中选择的元素时,命令能够响应。
7.注册空闲事件。Revit将使用此事件来通知命令Revit已准备好接收和显示其他结果数据。
8.启动一个新线程,该线程将用于计算和显示结果
a.填充描述将计算结果的表面位置的myUV元素列表
b.计算结果
i.将结果存储通过向“结果”IList中添加resultData类的实例来存储。每个resultData实例包含一个myUV和double值。
ii.当IList正在被修改时,将锁定结果列表,以防止多个线程同时修改它。
iii.为了简化此示例中的问题,使用了DateTime.Now属性的当前秒。
iv.Thread.Sleep用于创建1/2秒的暂停,模拟执行复杂分析所需的时间,并使此示例的各种功能更加显着。
说明:
1.打开MultithreadedCalculation.rvt。
2.运行外部命令。
3.选择示例文件中的体量、楼板或墙体。
4.Revit将向所选元素的最大面添加结果数据。
5.随时(在计算运行期间或计算完成后),修改元素的几何形状。之前的结果数据将被删除,并计算新的结果数据。
源代码
完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码
MultithreadedCalculation.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.Threading;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Analysis;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.UI.Selection;
using System.Diagnostics;
namespace Revit.SDK.Samples.MultithreadedCalculation.CS
{
/// <summary>
/// Command to set the target element and begin the multithreaded calculation.
/// </summary>
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class MultithreadedCalculation : IExternalCommand
{
static UpdaterId s_updaterId;
static int s_spatialFieldId;
static int s_oldSpatialFieldId;
static string s_docName;
static ElementId s_oldViewId;
static ElementId s_activeViewId;
public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIApplication uiApp = commandData.Application;
UIDocument uiDoc = uiApp.ActiveUIDocument;
Document doc = uiDoc.Document;
s_docName = doc.PathName;
Element element = null;
try
{
element = doc.GetElement(uiDoc.Selection.PickObject(ObjectType.Element, "Select an element for the AVF demonstration."));
}
catch (System.Exception)
{
message = "User aborted the tool.";
return Result.Cancelled;
}
// Set up SpatialFieldManager to hold results
s_activeViewId = doc.ActiveView.Id;
SpatialFieldManager oldSfm = null;
View oldView = null;
if (s_oldViewId != null) oldView = doc.GetElement(s_oldViewId) as View;
if (oldView != null) oldSfm = SpatialFieldManager.GetSpatialFieldManager(oldView);
// If a previous SFM was being managed, delete it
if (oldSfm != null) oldSfm.RemoveSpatialFieldPrimitive(s_oldSpatialFieldId);
// Setup container object for executing the calculation
MultithreadedCalculationContainer container = CreateContainer(element);
// Register updater to watch for geometry changes
SpatialFieldUpdater updater = new SpatialFieldUpdater(container,uiApp.ActiveAddInId);
if (!UpdaterRegistry.IsUpdaterRegistered(updater.GetUpdaterId()))
UpdaterRegistry.RegisterUpdater(updater, doc);
IList<ElementId> idCollection = new List<ElementId>();
idCollection.Add(element.Id);
UpdaterRegistry.RemoveAllTriggers(s_updaterId);
UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), doc, idCollection, Element.GetChangeTypeGeometry());
// Register idling event
uiApp.Idling += new EventHandler<IdlingEventArgs>(container.UpdateWhileIdling);
// Start new thread
Thread thread = new Thread(new ThreadStart(container.Run));
thread.Start();
return Autodesk.Revit.UI.Result.Succeeded;
}
/// <summary>
/// Prepares a container object that carries out the calculations without invoking Revit API calls.
/// </summary>
/// <param name="element">The element for the calculations.</param>
/// <returns>The container.</returns>
public static MultithreadedCalculationContainer CreateContainer(Element element)
{
Document doc = element.Document;
View activeView = doc.GetElement(s_activeViewId) as View;
// Figure out which is the largest face facing the user
XYZ viewDirection = activeView.ViewDirection.Normalize();
Face biggestFace = GetBiggestFaceFacingUser(element, viewDirection);
// Get or create SpatialFieldManager for AVF results
SpatialFieldManager sfm = SpatialFieldManager.GetSpatialFieldManager(activeView);
if (sfm == null) sfm = SpatialFieldManager.CreateSpatialFieldManager(activeView, 1);
// Reference the target face
s_spatialFieldId = sfm.AddSpatialFieldPrimitive(biggestFace.Reference);
// Compute the range of U and V for the calculation
BoundingBoxUV bbox = biggestFace.GetBoundingBox();
return new MultithreadedCalculationContainer(doc.PathName, bbox.Min, bbox.Max);
}
/// <summary>
/// Gets the biggest face which faces the user. Assumes that the element is a wall, or floor, or other "2-sided" element, and that
/// one of the two biggest faces will be facing roughly towards the viewer.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="viewDirection">The view direction.</param>
/// <returns>The face. Face.Reference will also be populated.</returns>
private static Face GetBiggestFaceFacingUser(Element element, XYZ viewDirection)
{
// Holds the faces sorted by area
SortedDictionary<double, List<Face>> faceAreas = new SortedDictionary<double, List<Face>>();
// Get the element geometry
Options options = new Options();
options.ComputeReferences = true;
GeometryElement geomElem = element.get_Geometry(options);
// Look at the faces in each solid
foreach (GeometryObject geomObj in geomElem)
{
Solid solid = geomObj as Solid;
if (solid != null)
{
foreach (Face face in solid.Faces)
{
double area = face.Area;
// Save the face to the collection
if (faceAreas.ContainsKey(area))
{
faceAreas[area].Add(face);
}
else
{
List<Face> faces = new List<Face>();
faces.Add(face);
faceAreas.Add(area, faces);
}
}
}
}
// Get biggest two faces. There might be two faces in the last item, or one face in the last item.
int count = faceAreas.Count;
KeyValuePair<double, List<Face>> faceCollection1 = faceAreas.ElementAt<KeyValuePair<double, List<Face>>>(count - 1);
KeyValuePair<double, List<Face>> faceCollection2 = faceAreas.ElementAt<KeyValuePair<double, List<Face>>>(count - 2);
Face face1 = null;
Face face2 = null;
// Two or more equal faces. Use the first two.
if (faceCollection1.Value.Count > 1)
{
face1 = faceCollection1.Value[0];
face2 = faceCollection1.Value[1];
}
// One largest face. Use the first face from the next item for comparison.
else
{
face1 = faceCollection1.Value[0];
face2 = faceCollection2.Value[0];
}
// Compute face normal
BoundingBoxUV box = face1.GetBoundingBox();
UV faceCenter = (box.Max + box.Min) / 2;
XYZ faceNormal = face1.ComputeNormal(faceCenter).Normalize();
// Compute angle to the view direction. If less than 90 degrees, keep this face.
double angle = viewDirection.AngleTo(faceNormal);
Face biggestFace = null;
if (Math.Abs(angle) < Math.PI / 2)
biggestFace = face1;
else
biggestFace = face2;
return biggestFace;
}
/// <summary>
/// Updater called when wall geometry changes, so analysis results can update.
/// </summary>
public class SpatialFieldUpdater : IUpdater
{
// The old container object.
MultithreadedCalculationContainer containerOld;
public SpatialFieldUpdater(MultithreadedCalculationContainer _container, AddInId addinId)
{
containerOld = _container;
s_updaterId = new UpdaterId(addinId, new Guid("FBF2F6B2-4C06-42d4-97C1-D1B4EB593EFF"));
}
// Execution method for the updater
public void Execute(UpdaterData data)
{
// Remove old idling event callback
UIApplication uiApp = new UIApplication(data.GetDocument().Application);
uiApp.Idling -= containerOld.UpdateWhileIdling;
containerOld.Stop();
// Clear the current AVF results
Document doc = data.GetDocument();
View activeView = doc.GetElement(s_activeViewId) as View;
SpatialFieldManager sfm = SpatialFieldManager.GetSpatialFieldManager(activeView);
sfm.Clear();
// Restart the multithread calculation with a new container
Element modifiedElem = doc.GetElement(data.GetModifiedElementIds().First<ElementId>());
MultithreadedCalculationContainer container = MultithreadedCalculation.CreateContainer(modifiedElem);
containerOld = container;
// Setup the new idling callback
uiApp.Idling += new EventHandler<IdlingEventArgs>(container.UpdateWhileIdling);
// Start the thread
Thread threadNew = new Thread(new ThreadStart(container.Run));
threadNew.Start();
}
public string GetAdditionalInformation() { return "AVF DMU Thread sample"; }
public ChangePriority GetChangePriority() { return ChangePriority.FloorsRoofsStructuralWalls; }
public UpdaterId GetUpdaterId() { return s_updaterId; }
public string GetUpdaterName() { return "AVF DMU Thread"; }
}
/// <summary>
/// Container class that manages the multithreaded calculation and idling activity.
/// </summary>
public class MultithreadedCalculationContainer
{
private volatile bool m_stop = false;
UV m_min;
UV m_max;
string m_docName;
IList<ResultsData> results = new List<ResultsData>();
IList<UV> m_uvToCalculate = new List<UV>();
int m_uvToCalculateCount;
IList<UV> uvPts = new List<UV>();
IList<ValueAtPoint> valList = new List<ValueAtPoint>();
public MultithreadedCalculationContainer(string _docName, UV _min, UV _max)
{
m_docName = _docName;
m_min = _min;
m_max = _max;
}
public void Run()
{
m_uvToCalculate = DetermineFacePoints();
m_uvToCalculateCount = m_uvToCalculate.Count;
Calculate();
}
/// <summary>
/// Stops the thread/calculation and application via idling.
/// </summary>
public void Stop()
{
m_stop = true;
}
/// <summary>
/// The idling callback which adds data to the AVF results.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void UpdateWhileIdling(object sender, IdlingEventArgs e)
{
UIApplication uiApp = sender as UIApplication;
// Get SpatialFieldManager
AnalysisResultSchema resultSchema = new AnalysisResultSchema("Schema Name", "Description");
SpatialFieldManager sfm = SpatialFieldManager.GetSpatialFieldManager(uiApp.ActiveUIDocument.Document.ActiveView);
if (sfm == null) sfm = SpatialFieldManager.CreateSpatialFieldManager(uiApp.ActiveUIDocument.Document.ActiveView, 1);
int schemaIndex = sfm.RegisterResult(resultSchema);
// If stopping, clear results and unset event.
if (m_stop)
{
lock (results)
{
results.Clear();
}
uiApp.Idling -= UpdateWhileIdling;
return;
}
// If document was closed and new document opened, do not run the update.
if (uiApp.ActiveUIDocument.Document.PathName == m_docName)
{
// Lock access to current calculated results
lock (results)
{
if (results.Count == 0) return;
// Turn each result to an AVF ValueAtPoint
foreach (ResultsData rData in results)
{
uvPts.Add(new UV(rData.UV.U, rData.UV.V));
IList<double> doubleList = new List<double>();
doubleList.Add(rData.Value);
valList.Add(new ValueAtPoint(doubleList));
}
FieldDomainPointsByUV pntsByUV = new FieldDomainPointsByUV(uvPts);
FieldValues fieldValues = new FieldValues(valList);
// Update with calculated values
Transaction t = new Transaction(uiApp.ActiveUIDocument.Document);
t.SetName("AVF");
t.Start();
if (!m_stop)
sfm.UpdateSpatialFieldPrimitive(s_spatialFieldId, pntsByUV, fieldValues, schemaIndex);
t.Commit();
// Clear results already processed.
results.Clear();
// If no more results to process, remove the idling event
if (m_uvToCalculateCount == 0)
{
uiApp.Idling -= UpdateWhileIdling;
s_oldViewId = s_activeViewId;
s_oldSpatialFieldId = s_spatialFieldId;
}
}
}
}
// Calculate the results in a loop
void Calculate()
{
foreach (UV uv in m_uvToCalculate)
{
if (m_stop)
{
m_uvToCalculateCount = 0;
return;
}
// Lock access to results while the data is added
lock (results)
{
results.Add(new ResultsData(uv, 1000 * Math.Sin(Math.Abs(uv.U * uv.V))));
Thread.Sleep(500); // to simulate the effect of a complex computation
m_uvToCalculateCount--;
}
}
}
private const int numberOfUPnts = 10;
private const int numberOfVPnts = 5;
// Setup the list of UV points to calculate results for
IList<UV> DetermineFacePoints()
{
IList<UV> uvList = new List<UV>();
double upnt = m_min.U;
double incrementU = (m_max.U - m_min.U) / (numberOfUPnts - 1);
double incrementV = (m_max.V - m_min.V) / (numberOfVPnts - 1);
while (upnt <= m_max.U)
{
double vpnt = m_min.V;
while (vpnt <= m_max.V)
{
uvList.Add(new UV(upnt,vpnt));
vpnt = vpnt + incrementV;
}
upnt = upnt + incrementU;
}
return uvList;
}
}
// Represents a set of results for the calculation
public class ResultsData
{
public UV UV;
public double Value;
public ResultsData(UV uv, double value)
{
this.UV = uv;
Value = value;
}
}
}
}
版权所有 :无锡模信建筑科技有限公司 苏ICP备2021028830号-1 BIM建模|BIM技术应用|BIM软件开发
联系地址:江苏省无锡市新吴区龙山路4号B座705 手机:18761516598