应用程序:SinePlotter (正弦函数绘图器)

Revit平台:全部

Revit版本:2013.0

首次发布时间:2013.0

编程语言:C#

技能水平:中等

类别:几何学、族

类型:ExternalCommand (外部命令) ExternalApplication (外部应用程序)

主题:简单正弦曲线演示

概要:

在正弦曲线上排列一系列棱柱族的实例。

相关类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.UI.IExternalApplication

项目文件:

Application.cs

它包含了类Application,负责设置插件的用户界面并处理各种用户输入。

Command.cs

它包含了类Command,该类从接口IExternalCommand继承并实现了Execute方法。Execute方法从插件的用户界面汇聚所需的输入参数,并调用FamilyInstancePlotter类中的方法,根据输入参数将族实例排列在正弦曲线上。

FamilyInstancePlotter.cs

它包含了放置对象实例的方法,根据输入参数在正弦曲线上放置对象的实例。

描述:

该示例提供以下功能:

- 允许用户选择属性值来定义一个正弦曲线,包括周期、振幅、周期数以及确定要在正弦曲线上放置多少个实例的每个正弦波的分区数。

- 允许用户从四种不同类型的棱柱中选择:圆柱棱柱、矩形棱柱、等腰三角形棱柱和正多边形棱柱。前提是这四种棱柱类型作为族符号加载到Revit项目文件中。使用文件夹中名为"CurvePlottingSample.rvt"的示例文件。

- 当单击运行按钮时,它在一个由用户输入参数定义的正弦曲线上排列一系列所选棱柱族实例。

说明:

使用CurvePlottingSample.rvt”示例文件打开Revit应用程序。

1. 在周期文本框中输入0.13.0之间的双精度值。

2. 在周期文本框中输入大于0的双精度值。

3. 在振幅文本框中输入-4.04.0之间的双精度值。

4. 在分区文本框中输入大于0的双精度值。

5. 从下拉菜单中选择棱柱类型。

6. 单击运行按钮

预期结果:选择的棱柱的若干个实例被排列在由用户输入参数定义的正弦曲线上。

源代码:

完整的源代码请加入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.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Media.Imaging;

using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Events;

namespace Revit.SDK.Samples.SinePlotter.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
{
private string assemblyPath;
private string assemblyName;
private string imageFolder;

private static double periodVal = 0.2;
private static double cyclesVal = 10;
private static double amplitudeVal = 3;
private static double partitionsVal = 3;
private static ComboBox prismComboBox;

private TextBox periodBox;
private TextBox cyclesBox;
private TextBox amplitudeBox;
private TextBox partitionsBox;

#region IExternalApplication Members
/// <summary>
/// Implements the OnShutdown event
/// </summary>
/// <param name="application"></param>
/// <returns>Indicates if the application completes its work successfully.</returns>
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}

/// <summary>
/// Implements the OnStartup event
/// </summary>
/// <param name="application"></param>
/// <returns>Indicates if the application completes its work successfully.</returns>
public Result OnStartup(UIControlledApplication application)
{
assemblyPath = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
assemblyName = Assembly.GetExecutingAssembly().GetName().Name + ".dll";
imageFolder = GetProjectDirectory() + "/Resources/";

//add a panel to go on the Add-In tab
RibbonPanel panel = application.CreateRibbonPanel("ArrayPrismsOnASineCurve");
//add panel with text fields for user input for period, amplitude and cycles of sine curve
AddCurvePropertiesTextFields(panel);
//add panel with text field for user input for number of partitions along the curve
AddPartitionsTextField(panel);
panel.AddSeparator();
//add drop down menu for type of prism selection
AddPrismComboBox(panel);
panel.AddSeparator();
//add RUN button
AddRunButton(panel);

return Result.Succeeded;
}

/// <summary>
/// Gets the name of the currently selected family symbol.
/// </summary>
/// <returns>A string representing the name of the currently selected family symbol</returns>
public static String GetFamilySymbolName()
{
return prismComboBox.Current.Name;
}

/// <summary>
/// Gets the value corresponding to the current text field input about the curve period.
/// </summary>
/// <returns>The double value corresponding to the curent text field input
/// for the curve period parameter.</returns>
public static double GetPeriod()
{
return periodVal;
}

/// <summary>
/// Gets the value corresponding to the current text field input about the curve
/// number of cycles.
/// </summary>
/// <returns>The double value corresponding to the curent text field input
/// for the curve number of cycles parameter.</returns>
public static double GetNumberOfCycles()
{
return cyclesVal;
}

/// <summary>
/// Gets the value corresponding to the current text field about the curve amplitude.
/// </summary>
/// <returns>The double value corresponding to the curent text field input
/// for the curve amplitude parameter.</returns>
public static double GetAplitude()
{
return amplitudeVal;
}

/// <summary>
/// Gets the value corresponding to the current text field about the number of partitions.
/// </summary>
/// <returns>The double value corresponding to the curent text field input for the
/// number of partitions.</returns>
public static double GetNumberOfPartitions()
{
return partitionsVal;
}

/// <summary>
/// Adds a drop down menu for selection of a type of prism. This four types
/// correspond to 4 family symbols already loaded in the working Revit document.
/// </summary>
/// <param name="panel">the RibbonPanel where the UI element is added</param>
private void AddPrismComboBox(RibbonPanel panel)
{
// create a four members combo box for family instance selection
//Family instance #1
ComboBoxMemberData comboBoxMemberData1 = new ComboBoxMemberData("cylinder", "cylinder prism");
//Family instance #2
ComboBoxMemberData comboBoxMemberData2 = new ComboBoxMemberData("rectangle", "rectangular prism");
//Family instance #3
ComboBoxMemberData comboBoxMemberData3 = new ComboBoxMemberData("regularpolygon", "regular polygon prism");
//Family instance #4
ComboBoxMemberData comboBoxMemberData4 = new ComboBoxMemberData("isotriangle", "isotriangle prism");

//make a combo box group group
ComboBoxData comboBxData = new ComboBoxData("ComboBox");
prismComboBox = panel.AddItem(comboBxData) as ComboBox;
prismComboBox.ToolTip = "select a prism to array on a curve";
prismComboBox.AddItem(comboBoxMemberData1);
prismComboBox.AddItem(comboBoxMemberData2);
prismComboBox.AddItem(comboBoxMemberData3);
prismComboBox.AddItem(comboBoxMemberData4);
}

/// <summary>
/// Adds a group of text fields for accepting user input for the various parameters
/// of the sine curve (cycles, period, amplitude).
/// </summary>
/// <param name="panel">the RibbonPanel where the UI element is added</param>
private void AddCurvePropertiesTextFields(RibbonPanel panel)
{
//Inactive textfields that just display information about the active input fields
TextBoxData periodLabelData = new TextBoxData("curve period field");
TextBoxData cyclesLabelData = new TextBoxData("curve cycles field");
TextBoxData amplitudeLabelData = new TextBoxData("curve amplitude field");
//Stack them horizontally
IList<RibbonItem> textFieldList = panel.AddStackedItems(periodLabelData, cyclesLabelData, amplitudeLabelData);
//Call method to customize the text boxes and make them inactive
String periodToolTip = "A double value denoting the period of the curve, i.e. how often the curve goes a full repition around the unit circle.";
CustomizeTextBox(panel, (TextBox)textFieldList[0], periodToolTip, "curve period:", 0, false, 130);
String cyclesToolTip = "A double value denoting the number of circles the curve makes.";
CustomizeTextBox(panel, (TextBox)textFieldList[1], cyclesToolTip, "curve cycles:", 0, false, 130);
String amplitudeToolTip = "A double value denoting how far the curve gets away from the x-axis.";
CustomizeTextBox(panel, (TextBox)textFieldList[2], amplitudeToolTip, "curve amplitude:", 0, false, 130);

//Active text fields for user input
TextBoxData periodBoxData = new TextBoxData("curve period");
TextBoxData cyclesBoxData = new TextBoxData("curve cycles");
TextBoxData amplitudeBoxData = new TextBoxData("curve amplitude");
//Stack them horizontally
IList<RibbonItem> textBoxList = panel.AddStackedItems(periodBoxData, cyclesBoxData, amplitudeBoxData);

periodBox = (TextBox)textBoxList[0];
cyclesBox = (TextBox)textBoxList[1];
amplitudeBox = (TextBox)textBoxList[2];

//Call method to customize the text boxes and make them active
CustomizeTextBox(panel, periodBox,
"Define the period of the sine curve", "" + periodVal, periodVal, true, 50);
CustomizeTextBox(panel, cyclesBox,
"Define the number of cycles of the sine curve", "" + cyclesVal, cyclesVal, true, 50);
CustomizeTextBox(panel, amplitudeBox,
"Define the amplitude of the sine curve", "" + amplitudeVal, amplitudeVal, true, 50);
}

/// <summary>
/// Add a text field for accepting user input for how many family instances to array on
/// the curve.
/// </summary>
/// <param name="panel">the RibbonPanel where the UI element is added</param>
private void AddPartitionsTextField(RibbonPanel panel)
{
TextBoxData partitionsFieldData = new TextBoxData("curve partitions field");
TextBox partitionsFieldBox = panel.AddItem(partitionsFieldData) as TextBox;
//Call method to customize the text box and set it as inactive
CustomizeTextBox(panel, partitionsFieldBox, null, "number of partitions:", 0, false, 130);
//Call method to customize the text box and set it as active
TextBoxData partitionsBoxData = new TextBoxData("curve partitions box");
partitionsBox = panel.AddItem(partitionsBoxData) as TextBox;
CustomizeTextBox(panel, partitionsBox, "Define the number of partitions", "" + partitionsVal, partitionsVal, true, 50);

}

/// <summary>
/// Adds button that arrays family instances of curves given the various
/// user inputs.
/// </summary>
/// <param name="panel">the RibbonPanel where the UI element is added</param>
private void AddRunButton(RibbonPanel panel)
{
PushButtonData pushButtonData = new PushButtonData("arrayPrisms", "run",
assemblyPath + "\\" + assemblyName, "Revit.SDK.Samples.SinePlotter.CS.Command");
pushButtonData.LargeImage = new BitmapImage(new Uri(imageFolder + "Start.png"));
PushButton pushButton = panel.AddItem(pushButtonData) as PushButton;
}

private void CustomizeTextBox(RibbonPanel panel, TextBox txtBox, String tip, String displayedText,
double defaultVal, Boolean isEnabled, int width)
{
txtBox.Value = displayedText;
txtBox.ToolTip = tip;
txtBox.Width = width;
txtBox.Enabled = isEnabled;

if (isEnabled)
{
txtBox.EnterPressed += new EventHandler<Autodesk.Revit.UI.Events.TextBoxEnterPressedEventArgs>(TextBoxEnterPressed);
}
}

/// <summary>
/// Handles the action of the text box user input. Checks if the input value is an
/// acceptable double value and if it lies among the acceptable range of values; if
/// it is it updates the corresponding field variable, if not it displays a warning
/// message to the user and retains the previous value.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">The event arguments used by ComboBox's CurrentChanged event.
/// </param>
private void TextBoxEnterPressed(object sender, TextBoxEnterPressedEventArgs e)
{
//cast sender as TextBox to retrieve text value
TextBox textBox = sender as TextBox;
double value;
if (double.TryParse(textBox.Value.ToString(), out value))
{
if ((textBox.Name).Equals(periodBox.Name))
{
if (value < 0.1 || value > 3) TaskDialog.Show("TextBox Input",
"The input value for " + textBox.Name + " has to be between 0.1 and 3.0");
else periodVal = value;
}
else if ((textBox.Name).Equals(cyclesBox.Name))
{
if (value <= 0) TaskDialog.Show("TextBox Input",
"The input value for " + textBox.Name + " has to be greater than zero.");
else cyclesVal = value;
}
else if ((textBox.Name).Equals(amplitudeBox.Name))
{
if (value < -4 || value > 4) TaskDialog.Show("TextBox Input",
"The input value for " + textBox.Name + " has to be between -4.0 and 4.0");
else amplitudeVal = value;
}
else
{
if (value <= 0) TaskDialog.Show("TextBox Input",
"The input value for " + textBox.Name + " has to be greater than zero.");
else partitionsVal = value;
}
}
else
{
TaskDialog.Show("TextBox Input", "The input value for " + textBox.Name + " has to be a double.");
}
}

/// <summary>
/// Returns the path of the main project directory.
/// </summary>
/// <returns>A string object corresponding to the full path of the main project directory.</returns>
private String GetProjectDirectory()
{
//get the absolut path of the assembly
string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//move two directory levels back
String grandParentDir = Directory.GetParent(assemblyPath).Parent.FullName;
return grandParentDir;
}

#endregion
}
}

FamilyInstancePlotter.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 Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB.Structure;
namespace Revit.SDK.Samples.SinePlotter.CS
{
    /// <summary>
    /// This class plots a number of instances of a given family object along a sine curve.
    /// </summary>
    class FamilyInstancePlotter
    {
        private FamilySymbol familySymbol;
        private Document document;
 
       
        /// <summary>
        /// The constructor for the FamilyInstancePlotter Class.
        /// </summary>
        /// <param name="fs">A Revit family symbol.</param>
        /// <param name="doc">The active Revit document.</param>
        public FamilyInstancePlotter(FamilySymbol fs, Document doc)
        {
            familySymbol = fs;
            document = doc;
        }
        /// <summary>
        /// Places a family instance at the given location.
        /// </summary>
        /// <param name="location">A point XYZ signifying the location for a family instance to be placed.</param>
        private void PlaceAtLocation(XYZ location)
        {
            Transaction t = new Transaction(document, "Place family instance");
            t.Start();
            FamilyInstance prism = document.Create.NewFamilyInstance(location, familySymbol,
                Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
            t.Commit();
        }
        /// <summary>
        /// Computes a sine curve taking into account input values for the sine curve period, amplitude and number 
        /// of circles and places a number of instances defined by the partitions input value of the same family 
        /// object along this curve.
        /// </summary>
        /// <param name="partitions">An integer value denoting the number of partitions per curve period.</param>
        /// <param name="period">A double value denoting the period of the curve, i.e. how often the curve
        /// goes a full repition around the unit circle.</param>
        /// <param name="amplitude">A double value denoting how far the curve gets away from the x-axis.</param>
        /// <param name="numOfCircles">A double value denoting the number of circles the curve makes.</param>
        public void PlaceInstancesOnCurve(int partitions, double period, double amplitude, double numOfCircles)
        {
            //Given the number of partitions compute the angle increment. 
            double theta = (2 * Math.PI) / partitions;
            TransactionGroup transGroup = new TransactionGroup(document, "Place All Instances");
            transGroup.Start();
            //Calculates the sine of an angle. This function expects the values of the angle parameter to be
            //provided in radians (values from 0 to 6.28). Values are returned in the range -1 to 1. The theta 
            //increment will give us at every iteration the point on the curve x for which we need to evaluate y.
            for (int i = 0; i <= partitions * numOfCircles; i++)
            {
                double x = i * theta;
                //function used:  y = a*sin(b*x)
                double y = (Math.Sin(period * x)) * amplitude;              
                XYZ temp = new XYZ(x, y, 0);
                
                PlaceAtLocation(temp);
            }
            transGroup.Assimilate();
        }
    }
}