Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Create user extesible funcionality

9 REPLIES 9
Reply
Message 1 of 10
Tripler123
320 Views, 9 Replies

Create user extesible funcionality

Hello,

 

This is more of a programming question than a Revit API question. I was creating a program that allows me to quantify elements. This is the code.

 

 

public double GetFormworkArea(Element el)
        {
			string elementDefinition = el.LookupParameter("element definition").AsString();
            switch (elementDefinition)
            {
				case "exterior Column":
					return getArea(el) / 2;
					break;
				case "interior Column":
					return getArea(el) * 3 / 2;
					break;
			}

			return 0;
        }

 

I have two cases inner column and outer column. But during the modeling process the user wants to create a new definition called "central column" with a formula get area()

How could I do for the user to add this functionality

 

9 REPLIES 9
Message 2 of 10
ricaun
in reply to: Tripler123

You probably need a database to save/edit all your elementDefinition cases. (Simple file should work, or you could save on the file using Extensible Storage)

 

And you need some kind of Expression Evaluator, similar to Revit Formula works.

 

Like this, the user can create any formula for each elementDefinition case.

 

Something like this for the file:

 

exterior Column = AREA / 2
interior Column = AREA * 3 / 2
central Column = AREA
my Column = AREA * AREA

 

Each line is one case and split by = separate the case with the formula.

 

That's an idea,

 

See yaa!

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 3 of 10
Sean_Page
in reply to: Tripler123

Not sure if it would work for you solution or not, but for these types of things I often use xml files and readers to provide scalability as well as project / owner specific needs.

 

I have attached a PDF example of something similar I had shared previously on LinkedIn.

 

Also a working example on my Git.

https://github.com/TorsionTools/R22

Message 4 of 10
Tripler123
in reply to: Tripler123

Thank you very much for your answers.

I will try it and comment my results.

Message 5 of 10
jeremy_tammik
in reply to: Tripler123

Since the Revit API is .NET based, and .NET provides powerful scripting support, why not make use of that?

 

For instance, search the Internet for '.net script':

 

https://duckduckgo.com/?q=.net+script

 

Here is an example script engine:

 

https://www.codeproject.com/articles/30999/scriptengine-user-defined-calculations-in-c-vb-jsc

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 6 of 10
ricaun
in reply to: Sean_Page

I always use JSON to save data, really simple to serialize and deserialize a c# object.

 

Revit already uses the Newtonsoft.Json package by default.

 

Here is an example: https://github.com/ricaun/RevitAddin.JsonExample.

 

@jeremy_tammik wrote:

Since the Revit API is .NET based, and .NET provides powerful scripting support, why not make use of that?

 

For instance, search the Internet for '.net script':

Interesting but I don't think is a good idea to give the user the ability to run any C# code, looks dangerous.

 

This ExpressionParser looks that gonna work.

 

public class ExpressionParser
{
    /// <summary>
    /// eval
    /// Source: https://stackoverflow.com/questions/3972854/parse-math-expression
    /// </summary>
    /// <param name="exp"></param>
    /// <param name="vars"></param>
    /// <returns></returns>
    public double eval(string exp, Dictionary<string, double> vars)
    {
        int bracketCounter = 0;
        int operatorIndex = -1;

        exp = exp.Trim();

        for (int i = 0; i < exp.Length; i++)
        {
            char c = exp[i];
            if (c == '(') bracketCounter++;
            else if (c == ')') bracketCounter--;
            else if ((c == '+' || c == '-') && bracketCounter == 0)
            {
                operatorIndex = i;
                break;
            }
            else if ((c == '*' || c == '/') && bracketCounter == 0 && operatorIndex < 0)
            {
                operatorIndex = i;
            }
        }
        if (operatorIndex < 0)
        {
            exp = exp.Trim();
            if (exp[0] == '(' && exp[exp.Length - 1] == ')')
                return eval(exp.Substring(1, exp.Length - 1), vars);
            else if (vars.ContainsKey(exp))
                return vars[exp];
            else
                return double.Parse(exp);
        }
        else
        {
            switch (exp[operatorIndex])
            {
                case '+':
                    return eval(exp.Substring(0, operatorIndex), vars) + eval(exp.Substring(operatorIndex + 1), vars);
                case '-':
                    return eval(exp.Substring(0, operatorIndex), vars) - eval(exp.Substring(operatorIndex + 1), vars);
                case '*':
                    return eval(exp.Substring(0, operatorIndex), vars) * eval(exp.Substring(operatorIndex + 1), vars);
                case '/':
                    return eval(exp.Substring(0, operatorIndex), vars) / eval(exp.Substring(operatorIndex + 1), vars);
            }
        }
        return 0;
    }
}

With this simple code to test!

 

var expressionParser = new ExpressionParser();
var vars = new Dictionary<string, double>();
vars.Add("AREA", 10.0);
var area = expressionParser.eval("AREA * AREA", vars);
System.Windows.MessageBox.Show($"AREA {area}");

 

See yaa!

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 7 of 10
RPTHOMAS108
in reply to: Tripler123

You may find FormulaManager.Evaluate offers a more Revit centric approach.

 

However with the above parameterId as ElementId seems to imply a parameter element is required. Likely to establish the parameter type of the result. 

 

"It evaluates formula using list of global or family parameters depends on document type. "

 

That isn't that clear: probably however means you have to be in a family document to evaluate a family parameter and a project to evaluate a global one. I guess you could make it work via adding what you need in a temporary way if it is requiring a parameter of some kind i.e. a global one in project (although you wouldn't be able to reference other parameter names in the formula string).

 

 

Message 8 of 10
RPTHOMAS108
in reply to: RPTHOMAS108

A simple example that works:

 

Public Function Obj_220118a(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result

        Dim app = commandData.Application
        Dim uidoc = commandData.Application.ActiveUIDocument
        Dim IntDoc = uidoc.Document

        Dim Formula As String = "(10*10)^0.5"
        Dim Formula0 As String = "Pi()"
        Dim Out As String = ""

        Using Tx As New Transaction(IntDoc, "XX")
            If Tx.Start Then

                Dim G As String = Guid.NewGuid.ToString
                Dim GP = GlobalParameter.Create(IntDoc, "RPT_" & G, SpecTypeId.Number)
                Out = FormulaManager.Evaluate(GP.Id, IntDoc, Formula0)

                Tx.RollBack()
            End If
        End Using

        TaskDialog.Show("Result", Out)

        Return Result.Succeeded

    End Function

 

 

Message 9 of 10
Tripler123
in reply to: RPTHOMAS108

 

I thought that I could use as the quantification tables, but it didn't work and I had an error with the following code.

 

public Result Execute(ExternalCommandData commandData,
                              ref string message,
                              ElementSet elements)
        {

            UIApplication uiApp = commandData.Application;
            UIDocument uiDoc = uiApp.ActiveUIDocument;
            Document doc = uiDoc.Document;

            ElementId elementId = uiDoc.Selection.PickObject(ObjectType.Element, "Select element").ElementId;

            //IList<string> functions =  FormulaManager.GetFunctions();

            try
            {
                string result = FormulaManager.Evaluate(elementId, doc, "Volume * 2");
                TaskDialog.Show("Lambda Ingenieria e Innovacion", result);
            }
            catch (Exception ex)
            {
                TaskDialog.Show("Lambda Ingenieria e Innovacion", ex.Message);
                return Result.Cancelled;

            }
            return Result.Succeeded;
        }

 

Message 10 of 10
RPTHOMAS108
in reply to: Tripler123

No you need to provide the id to a FamilyParameter or GlobalParameter (GlobalParameter in Project or FamilyParameter in Family) and not an Element or parameter you get from Element.Parameter.

 

A further alternative would be if you had a known empty family that is added to a project. The user could add formulas to that and you could then change type parameter value inputs for those formulas in the project family type to get an output that way.

So in your situation you would add a type parameter to the empty family named 'Area'. The user would add separate type parameters to same family with formulas:

Parameter name = ExtColumn, Formula = Area / 2
Parameter name =  InteriorColumn, Formula = Area * 1.5

...

If you then edit the type and populate the 'Area' parameter then you can read off the appropriate named parameter value (ExtColumn or InteriorColumn...). 'Area' could be a shared parameter that is not user modifiable and you should find it by GUID not it's name 'Area'.

 

Although the approach I outlined initially above seems to work and is likely the most straightforward option. The loaded family option has the benefit of not having to store the formulas in some separate settings file nor create a UI for editing such i.e. it is all inherently Revit.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community