Code Crit

Code Crit

Mark.Ackerley
Advocate Advocate
971 Views
2 Replies
Message 1 of 3

Code Crit

Mark.Ackerley
Advocate
Advocate

Hi,

 

I'm very much a beginner in C#, I have written this code and would be grateful for any and all comments (please be gentle!). 

 

I have a ribbon button method which assesses dimensions in a view according to whether they are brick-dimensions... Overriding by colour and adding a suffix.  I also have a second method (called from a WPF button) which returns a dialogue to aid users if they have a non-brick dimension (there are fewer cross overs than you might think!).

 

I would particularly appreciate advice as to structuring my code, handling exceptions and speed enhancements... I am currently filtering through every dimension in the project to see which are visible in the active view.  The project might have 10k, but I am only interested in 10... It seems inefficient and slow!  I will probably change to a user drag selection if there is no better alternative.

 

I am not really looking for suggestions as to how to find whether a dimension is a brick-dimension, I have explored various methods and I am happy with this.

 

I have obviously copied and adjusted many people's code to generate this, thank you everyone for your help.

 

Cheers,

 

Mark

 

using System;
using System.Text;
using System.Linq;

using System.Collections.Generic;

using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;


namespace BrickDims
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    public class BrickDimsColourOverride : IExternalCommand
    {

        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            BrickDimsColour(commandData.Application.ActiveUIDocument);

            return Result.Succeeded;
        }



        private void Module_Startup(object sender, EventArgs e)
        {

        }

        private void Module_Shutdown(object sender, EventArgs e)
        {

        }


        public void BrickDimsColour(UIDocument uidoc)
        {
            //filtered element collector dimensions in active view
            //create graphic settings to override dims in view

            //CO
            OverrideGraphicSettings ogsCO = new OverrideGraphicSettings();
            ogsCO.SetProjectionLineColor(new Color(100, 200, 100)); //green

            //CO minus
            OverrideGraphicSettings ogsCOminus = new OverrideGraphicSettings();
            ogsCOminus.SetProjectionLineColor(new Color(100, 100, 200)); //blue

            //CO plus
            OverrideGraphicSettings ogsCOplus = new OverrideGraphicSettings();
            ogsCOplus.SetProjectionLineColor(new Color(200, 200, 50)); //yellow

            //Cut
            OverrideGraphicSettings ogsCut = new OverrideGraphicSettings();
            ogsCut.SetProjectionLineColor(new Color(200, 0, 0)); //red

            //Joint
            OverrideGraphicSettings ogsJoint = new OverrideGraphicSettings();
            ogsJoint.SetProjectionLineColor(new Color(128, 0, 128)); //purple

            //Multi
            OverrideGraphicSettings ogsMulti = new OverrideGraphicSettings();
            ogsMulti.SetProjectionLineColor(new Color(255, 192, 203)); //pink


            Transaction transaction = new Transaction(uidoc.Document);
            transaction.Start("Colour Dims By Brick Dim");

            List<Element> linearDimensions = new FilteredElementCollector(uidoc.Document).OfCategory(BuiltInCategory.OST_Dimensions).ToList();

            //error catching list
            List<Element> errorCatch = new List<Element>();

            //create lists of dimensions
            foreach (Dimension dim in linearDimensions)
            {
                bool test = Container.IsElementVisibleInView(uidoc.ActiveView, dim);
                if (test == true)
                {
                    //if empty add to cut brick list list
                    //we could also check for multi dims and flag to user, poss with split method...
                    if (dim.Value == null)
                    {
                        uidoc.ActiveView.SetElementOverrides(dim.Id, ogsJoint);
                        //we could probably override suffixes here if we wanted...dim.Suffix = "Joint";
                        errorCatch.Add(dim);
                    }

                    else
                    {
                        //get dimension value in mm
                        double dimValue = Convert.ToDouble(dim.Value);
                        double dimValueMm = UnitUtils.ConvertFromInternalUnits(dimValue, UnitTypeId.Millimeters);
                        double dimValueMmRnd = BrickFuncts.round(dimValueMm); //round to 1 dp

                        if (dimValueMmRnd == 10)
                        {
                            //if value == 10, add item to cut brick list
                            uidoc.ActiveView.SetElementOverrides(dim.Id, ogsJoint);
                            dim.Suffix = "Joint";
                            errorCatch.Add(dim);
                        }

                        else
                        {
                            //divide by CO
                            double dimCO = dimValueMmRnd / BrickFuncts.CO(1);
                            //get math floor and set of CO values
                            double dimCOflr = Math.Floor(dimCO);

                            double dimCOflrCO = BrickFuncts.round(BrickFuncts.CO(dimCOflr));
                            double dimCOflrCOminus = BrickFuncts.round(BrickFuncts.COMinus(dimCOflr));
                            double dimCOflrCOPlus = BrickFuncts.round(BrickFuncts.COPlus(dimCOflr));

                            //get math ceiling and set of CO values
                            double dimCOclng = Math.Ceiling(dimCO);

                            double dimCOclngCO = BrickFuncts.round(BrickFuncts.CO(dimCOclng));
                            double dimCOclngCOminus = BrickFuncts.round(BrickFuncts.COMinus(dimCOclng));
                            double dimCOclngCOPlus = BrickFuncts.round(BrickFuncts.COPlus(dimCOclng));

                            if ((dimValueMmRnd == dimCOflrCO) || (dimValueMmRnd == dimCOclngCO))
                            {
                                dim.Suffix = "CO";
                                uidoc.ActiveView.SetElementOverrides(dim.Id, ogsCO);
                                errorCatch.Add(dim);
                            }

                            else if ((dimValueMmRnd == dimCOflrCOminus) || (dimValueMmRnd == dimCOclngCOminus))
                            {
                                dim.Suffix = "CO-";
                                uidoc.ActiveView.SetElementOverrides(dim.Id, ogsCOminus);
                                errorCatch.Add(dim);
                            }

                            else if ((dimValueMmRnd == dimCOflrCOPlus) || (dimValueMmRnd == dimCOclngCOPlus))
                            {
                                dim.Suffix = "CO+";
                                uidoc.ActiveView.SetElementOverrides(dim.Id, ogsCOplus);
                                errorCatch.Add(dim);
                            }

                            else
                            {
                                dim.Suffix = "cut";
                                uidoc.ActiveView.SetElementOverrides(dim.Id, ogsCut);
                                errorCatch.Add(dim);
                            }

                        }

                        //sb.AppendLine(dimValueMmRnd.ToString());
                    }
                }
            }
            //mainDialog.MainInstruction = sb.ToString();
            //TaskDialogResult tResult = mainDialog.Show();
            if (errorCatch.Count == 0)
            {
                TaskDialog.Show("fail", "oops that didn't work, perhaps try another view? or adding some dimensions?");
            }

            transaction.Commit();

        }

    }

    internal class Methods
    {
        public static void BrickDimCheck(Ui ui, UIDocument uidoc, Document doc)
        {
            ISelectionFilter dimsFilt = new SelectionFilterDims();
            ElementId dimId = uidoc.Selection.PickObject(ObjectType.Element, dimsFilt).ElementId;
            Dimension dim = doc.GetElement(dimId) as Dimension;

            StringBuilder sb = new StringBuilder();

            //if empty add to cut brick list list
            //we could also check for multi dims and flag to user, poss with split method...
            if (dim.Value == null)
            {
                sb.Append("Something went wrong, perhaps this is a multi-dim?");
                ui.TbDebug.Content = sb.ToString();
                //we could probably override suffixes here if we wanted...dim.Suffix = "Joint";
                //this is probably a multi one, we could give a list?!
            }

            else
            {
                //get dimension value in mm
                double dimValue = Convert.ToDouble(dim.Value);
                double dimValueMm = UnitUtils.ConvertFromInternalUnits(dimValue, UnitTypeId.Millimeters);
                double dimValueMmRnd = BrickFuncts.round(dimValueMm); //round to 1 dp

                //divide by CO
                double dimCO = dimValueMmRnd / BrickFuncts.CO(1);
                //get math floor and set of CO values
                double dimCOflr = Math.Floor(dimCO);

                double dimCOflrCO = BrickFuncts.round(BrickFuncts.CO(dimCOflr));
                double dimCOflrCOminus = BrickFuncts.round(BrickFuncts.COMinus(dimCOflr));
                double dimCOflrCOPlus = BrickFuncts.round(BrickFuncts.COPlus(dimCOflr));

                //get math ceiling and set of CO values
                double dimCOclng = Math.Ceiling(dimCO);

                double dimCOclngCO = BrickFuncts.round(BrickFuncts.CO(dimCOclng));
                double dimCOclngCOminus = BrickFuncts.round(BrickFuncts.COMinus(dimCOclng));
                double dimCOclngCOPlus = BrickFuncts.round(BrickFuncts.COPlus(dimCOclng));

                sb.Append("CO-\t\tCO\t\tCO+\n");
                sb.Append(dimCOflrCOminus.ToString() + "\t\t" + dimCOflrCO.ToString() + "\t\t" + dimCOflrCOPlus.ToString()+"\n");
                sb.Append(dimCOclngCOminus.ToString() + "\t\t" + dimCOclngCO.ToString() + "\t\t" + dimCOclngCOPlus.ToString());

                ui.TbDebug.Content = sb.ToString();
                ui.DimSelClick.Text = dimValueMmRnd.ToString();
            }
        }
    }

    public static class BrickFuncts
    {
        //base input CO-
        public static double brick = 102.5;

        //CO for x bricks
       public static double CO(double a)
        {
            return (brick + 10) * a;
        }

        //co for y bricks
        public static double COPlus(double a)
        {
            return CO(a) + 10;
        }

        //co- for z bricks
        public static double COMinus(double a)
        {
            return CO(a) - 10;
        }

        public static double round(double a)
        {
            return (Math.Round(a * 10)) / 10;
        }

    }

    public static class Container
    {
        public static bool IsElementVisibleInView(View view, Element el)
        {
            if (view == null)
            {
                TaskDialog nullEx = new TaskDialog("That didn't work, perhaps this isn't a view with dimensions?");
                TaskDialogResult nullRes = nullEx.Show();
                throw new ArgumentNullException(nameof(view));
            }

            if (el == null)
            {
                TaskDialog nullEx = new TaskDialog("That didn't work, perhaps you didn't select a dimension?");
                TaskDialogResult nullRes = nullEx.Show();
                throw new ArgumentNullException(nameof(el));
            }

            // Obtain the element's document.

            Document doc = el.Document;

            ElementId elId = el.Id;

            // Create a FilterRule that searches 
            // for an element matching the given Id.

            FilterRule idRule = ParameterFilterRuleFactory
                .CreateEqualsRule(
                new ElementId(BuiltInParameter.ID_PARAM),
                elId);

            var idFilter = new ElementParameterFilter(idRule);

            // Use an ElementCategoryFilter to speed up the 
            // search, as ElementParameterFilter is a slow filter.

            Category cat = el.Category;
            var catFilter = new ElementCategoryFilter(cat.Id);

            // Use the constructor of FilteredElementCollector 
            // that accepts a view id as a parameter to only 
            // search that view.
            // Also use the WhereElementIsNotElementType filter 
            // to eliminate element types.

            FilteredElementCollector collector =
                new FilteredElementCollector(doc, view.Id)
                    .WhereElementIsNotElementType()
                    .WherePasses(catFilter)
                    .WherePasses(idFilter);

            // If the collector contains any items, then 
            // we know that the element is visible in the
            // given view.

            return collector.Any();
        }
    }

    public class SelectionFilterDims : ISelectionFilter
    {
        public bool AllowElement(Element elem)
        {
            if (elem is Dimension) return true;
            return false;
        }
        public bool AllowReference(Reference refer, XYZ pos)
        {
            return false;
        }
    }
}

 

 

0 Likes
Accepted solutions (1)
972 Views
2 Replies
Replies (2)
Message 2 of 3

RPTHOMAS108
Mentor
Mentor
Accepted solution

Dimensions are view based so if you are running this on the active view then you can supply the ElementId of the active view to the FilteredElementCollector. By doing this you'll have only those dimensions in the active view and therefore you would not have to check if the dimension is visible in the view. I don't know if you are also checking that the dimensions are attached to walls rather than other elements, this can be done by inspection of references.

 

If you are running the command on a selected set of views you can loop through those views in turn doing the above.

 

I would not be throwing exceptions since every exception thrown has to be caught by someone. That someone is going to be you unless you are writing an assembly referenced by other developers. Task dialogs conveying messages to the end user are better. Taskdialog tells user there is something wrong with what they did, unhandled exception tells user there is something wrong with what you did.

 

Transactions are better when you follow the 'Using' pattern this ensures the object is disposed of sooner.

 

Use FilteredElementCollector.ToElements or ToElemenIds instead of .ToList. (.ToList is a Linq extension method to IEnumerable<T> and is therefore not RevitAPI centric.). By all accounts it works however.

 

Some refactoring is possible on the override graphics since you are changing only the colour of projection lines and repeating the same code to do this (including suffixing of dims). You could use a function that calls the same code with different colour values / suffixes. However there are more than one way of doing these things and they often come down to personal preference. The only performance benefit is probably the first paragraph regarding filtering by view instead of entire database.

0 Likes
Message 3 of 3

Mark.Ackerley
Advocate
Advocate

Fantastic, thanks for all that.

 

I'll have a go and report back!

 

0 Likes