Announcements
Attention for Customers without Multi-Factor Authentication or Single Sign-On - OTP Verification rolls out April 2025. Read all about it here.

Fixing Dimension in Assembly View: Prevent Value Change When Moved in Revit API

DesignGroup01
Enthusiast

Fixing Dimension in Assembly View: Prevent Value Change When Moved in Revit API

DesignGroup01
Enthusiast
Enthusiast

Hello All,

I am working on a project where I need to add dimensions to an edge in an assembly view. The dimensions are generated using Revit API code, and everything seems to work fine initially. However, when I manually move the dimension in the view, the dimension value changes to zero, which is not the desired behavior.

Issue:

  • The dimension is placed on the edge of an object in an assembly view.(image Before)
  • When I move the dimension manually in the view, it changes to zero, even though the geometry and edge position remain unchanged.(image After)
  • The goal is to prevent the dimension value from changing when the dimension is moved in the view.

What I've Tried:

  • The dimension is generated using Revit API code and is initially set correctly.
  • I've confirmed that the view is an assembly view.
  • When moving the dimension manually, the dimension value becomes zero.

Desired Behavior:

  • The dimension should remain consistent, and its value should not change when moved.
  • The value should reflect the correct measurement between the two reference points, even if the dimension line is manually adjusted in the view.

I would appreciate any advice or suggestions on how to handle this issue programmatically to ensure that the dimension value remains fixed even when moved manually.

Thank you in advance for your help!

 

using System;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System.Windows;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace MGL.Core.Create.ShopDrawing
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class ShopdrawingDimension_Command : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            try
            {
                UIApplication uiapp = commandData.Application;
                UIDocument uiDoc = uiapp.ActiveUIDocument;
                Document doc = uiDoc.Document;

                // Step 1: Select a viewport from the sheet
                Viewport viewport = SelectViewport(uiDoc);
                if (viewport == null)
                {
                    TaskDialog.Show("Cancelled", "No viewport selected.");
                    return Result.Cancelled;
                }

                // Step 2: Get the view associated with the selected viewport
                View view = doc.GetElement(viewport.ViewId) as View;
                if (view == null)
                {
                    TaskDialog.Show("Error", "The selected viewport does not have a valid view.");
                    return Result.Failed;
                }

                // Step 3: Get the visible walls in the view
                var wallsInView = GetWallsInView(doc, view);
                if (!wallsInView.Any())
                {
                    TaskDialog.Show("No Walls", "No walls found in the selected view.");
                    return Result.Cancelled;
                }

                using (Transaction tx = new Transaction(doc, "Add Dimensions to Slanted Walls"))
                {
                    tx.Start();

                    foreach (var wall in wallsInView)
                    {
                        // Add dimensions to each wall in the view
                        AddDimensionsToWall(doc, view, wall);
                    }

                    tx.Commit();
                }

                TaskDialog.Show("Success", "Dimensions added to the selected walls.");
                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                TaskDialog.Show("Execution Failed", $"Error: {ex.Message}");
                message = ex.Message;
                return Result.Failed;
            }
        }


        private Viewport SelectViewport(UIDocument uiDoc)
        {
            try
            {
                // Select a viewport from the sheet
                Reference reference = uiDoc.Selection.PickObject(ObjectType.Element, new ViewportSelectionFilter(), "Select a viewport from the sheet");
                Element element = uiDoc.Document.GetElement(reference);
                return element as Viewport;
            }
            catch
            {
                return null;
            }
        }

        private List<Wall> GetWallsInView(Document doc, View view)
        {
            // Get walls visible in the selected view
            return new FilteredElementCollector(doc, view.Id)
                .OfClass(typeof(Wall))
                .Cast<Wall>()
                .ToList();
        }

        private bool IsFaceVisibleInView(PlanarFace face, View view)
        {
            // Check if the face is visible in the current view
            XYZ faceNormal = face.FaceNormal;
            XYZ viewDirection = view.ViewDirection.Normalize();

            // The face is visible if its normal is perpendicular to the view direction
            return Math.Abs(faceNormal.DotProduct(viewDirection)) < 0.01;
        }





        private void AddDimensionsToWall(Document doc, View view, Wall wall)
        {
            // Get the geometry of the wall
            Options options = new Options
            {
                ComputeReferences = true,
                View = view
            };
            GeometryElement geometryElement = wall.get_Geometry(options);

            foreach (GeometryObject geometryObject in geometryElement)
            {
                if (geometryObject is Solid solid)
                {
                    // Iterate through the faces of the solid
                    foreach (Face face in solid.Faces)
                    {
                        if (face is PlanarFace originalFace)
                        {
                            // Apply the transformation to align the face normal with the view direction
                            PlanarFace alignedFace = AlignFaceWithView(originalFace, view);

                            // Check if the aligned face is visible in the view
                            if (IsFaceVisibleInView(alignedFace, view))
                            {
                                // Create dimensions for the aligned face
                                CreateDimensionsForFace(doc, view, alignedFace);
                            }
                        }
                    }
                }
            }
        }


        private Transform GetTransformationToAlignFaceWithView(PlanarFace face, View view)
        {
            XYZ faceNormal = face.FaceNormal;
            XYZ viewDirection = view.ViewDirection.Normalize();

            // If face normal is already aligned with the view direction, no transformation is needed
            if (Math.Abs(faceNormal.DotProduct(viewDirection)) > 0.999)
                return null;

            // Create a rotation transformation to align the face normal with the view direction
            XYZ rotationAxis = faceNormal.CrossProduct(viewDirection).Normalize();
            double angle = faceNormal.AngleTo(viewDirection);

            // Create and return the transformation
            return Transform.CreateRotationAtPoint(rotationAxis, angle, face.Origin);
        }



        private PlanarFace AlignFaceWithView(PlanarFace originalFace, View view)
        {
            XYZ faceNormal = originalFace.FaceNormal;
            XYZ viewDirection = view.ViewDirection.Normalize();

            // If the face normal is already aligned with the view direction, return the original face
            if (Math.Abs(faceNormal.DotProduct(viewDirection)) > 0.999)
                return originalFace;

            // Create a rotation transformation to align the face normal with the view direction
            XYZ rotationAxis = faceNormal.CrossProduct(viewDirection).Normalize();
            double angle = faceNormal.AngleTo(viewDirection);

            // Create the rotation transformation
            Transform rotationTransform = Transform.CreateRotationAtPoint(rotationAxis, angle, originalFace.Origin);

            // Apply the rotation to the face geometry (manually transform the geometry)
            foreach (Edge edge in originalFace.EdgeLoops.get_Item(0))
            {
                // Transform each edge of the face
                Curve curve = edge.AsCurve();
                curve = curve.CreateTransformed(rotationTransform);
            }

            // Return the transformed face
            return originalFace;
        }

        private void CreateDimensionsForFace(Document doc, View view, PlanarFace face)
        {
            // Get bounding edges of the face
            EdgeArray edges = face.EdgeLoops.get_Item(0);

            // Collect references for dimensioning
            ReferenceArray references = new ReferenceArray();
            List<XYZ> edgePoints = new List<XYZ>();

            foreach (Edge edge in edges)
            {
                Curve curve = edge.AsCurve();
                if (curve is Line line)
                {
                    // Add edge reference to the array
                    references.Append(edge.Reference);

                    // Add the endpoints of the line for dimensioning
                    edgePoints.Add(line.GetEndPoint(0));
                    edgePoints.Add(line.GetEndPoint(1));
                }
            }

            // Ensure at least two references for a valid dimension
            if (references.Size < 2 || edgePoints.Count < 2)
            {
                TaskDialog.Show("Error", "Not enough references to create a dimension.");
                return;
            }

            // Determine dimension line direction based on edge points
            XYZ dimensionStart = edgePoints.First();
            XYZ dimensionEnd = edgePoints.Last();

            // Calculate the direction between the points
            XYZ dimensionDirection = dimensionEnd - dimensionStart;

            // Debugging: Show dimension line information
            TaskDialog.Show("Dimension Info", $"Start: {dimensionStart}\nEnd: {dimensionEnd}\nDirection: {dimensionDirection}");

            // Ensure the direction is valid for dimensioning (avoid small magnitude)
            if (dimensionDirection.IsZeroLength())
            {
                TaskDialog.Show("Error", "The start and end points are identical, invalid dimension direction.");
                return;
            }

            // Define a minimum threshold for valid dimension size
            double minDimensionSize = 0.01;  // Example: 0.01 units as the threshold (can be adjusted)

            // Check if the dimension size is too small
            if (dimensionDirection.GetLength() < minDimensionSize)
            {
                TaskDialog.Show("Warning", "The dimension size is too small to be valid.");
                return; // Skip creating dimension for too small sizes
            }

            // Check that the direction vector is valid and non-zero
            if (dimensionDirection.IsZeroLength() || dimensionDirection.DotProduct(XYZ.BasisZ) < 0.001)
            {
                TaskDialog.Show("Error", "The dimension direction is invalid. Please ensure the edge is valid for dimensioning.");
                return;
            }

            // Proceed with dimension creation
            Line dimensionLine = Line.CreateBound(dimensionStart, dimensionEnd);
            

            // Create the dimension using the line direction
            doc.Create.NewDimension(view, dimensionLine, references);
        }







        /// <summary>
        /// Check if a face normal aligns with the view's direction.
        /// </summary>
        private bool IsAlignedWithView(View view, XYZ normal)
        {
            if (view is ViewPlan || view is ViewSection)
            {
                // Get the view direction
                XYZ viewDirection = view.ViewDirection.Normalize();

                // Calculate the dot product between the face normal and view direction
                double dotProduct = Math.Abs(viewDirection.DotProduct(normal));

                // Log values for debugging
                TaskDialog.Show("Debug Info", $"Face Normal: {normal}\nView Direction: {viewDirection}\nDot Product: {dotProduct}");

                // Allow more tolerance for alignment (e.g., 0.95 instead of 0.99)
                return dotProduct > 0.95; // More tolerance for face alignment
            }

            return true; // Assume alignment for other view types
        }




        /// <summary>
        /// Check if a line lies in the plane of the view.
        /// </summary>
        private bool IsLineInViewPlane(View view, Line line)
        {
            // Get the plane of the view
            Plane viewPlane = view.SketchPlane?.GetPlane();
            if (viewPlane == null) return false;

            // Validate each endpoint of the line
            return IsPointOnPlane(viewPlane, line.GetEndPoint(0)) && IsPointOnPlane(viewPlane, line.GetEndPoint(1));
        }

        /// <summary>
        /// Verify if the face normal aligns with the view direction.
        /// </summary>
        private bool IsFaceNormalAlignedWithView(View view, PlanarFace face)
        {
            // Get the view's direction
            XYZ viewDirection = view.ViewDirection.Normalize();

            // Get the face's normal
            XYZ faceNormal = face.FaceNormal.Normalize();

            // Check alignment (parallel or antiparallel)
            double dotProduct = faceNormal.DotProduct(viewDirection);

            // Allow small tolerance for floating-point comparison
            return Math.Abs(dotProduct) > 0.999; // 0.999 allows for minimal deviation
        }


        /// <summary>
        /// Check if a point lies on a given plane.
        /// </summary>
        private bool IsPointOnPlane(Plane plane, XYZ point)
        {
            // Vector from the plane's origin to the point
            XYZ vector = point - plane.Origin;

            // Check if the vector is perpendicular to the plane's normal
            return Math.Abs(plane.Normal.DotProduct(vector)) < 1e-6; // Tolerance for floating-point comparison
        }



        private class ViewportSelectionFilter : ISelectionFilter
        {
            public bool AllowElement(Element element) => element is Viewport;
            public bool AllowReference(Reference reference, XYZ position) => false;
        }
       

        
    }
}

 



0 Likes
Reply
Accepted solutions (1)
282 Views
5 Replies
Replies (5)

Sleepingfish_Kuo
Enthusiast
Enthusiast
                    // Add the endpoints of the line for dimensioning
                    edgePoints.Add(line.GetEndPoint(0));
                    edgePoints.Add(line.GetEndPoint(1));

 

GetEndPoint will get a copy of it, so it lose when you do any change.

Try these instead.

                    List<ReferenceArray> refArrayPairs = new();

                    foreach (Edge edge in edges)
                    {
                        Curve curve = edge.AsCurve();
                        // create a pair
                        ReferenceArray refArrayPair = new ReferenceArray();
                        if (curve is Line line)  //Maybe an arc is fine, too.
                        {
                            // Add the endpoints of the line for dimensioning
                            refArrayPair.Append(curve.GetEndPointReference(0));
                            refArrayPair.Append(curve.GetEndPointReference(1));
                        }
                        if (refArrayPair.Size == 2)
                            refArrayPairs.Add(refArrayPair);
                    }

*And then the dimension creating code should edit, too.

 

0 Likes

DesignGroup01
Enthusiast
Enthusiast

Thanks for your reply.

private void CreateDimensionsForFace(Document doc, View view, PlanarFace face)
{
    // Get bounding edges of the face
    EdgeArray edges = face.EdgeLoops.get_Item(0);

    // Collect references for dimensioning
    ReferenceArray references = new ReferenceArray();
    List<XYZ> edgePoints = new List<XYZ>();

    List<ReferenceArray> refArrayPairs = new();

    foreach (Edge edge in edges)
    {
        Curve curve = edge.AsCurve();
        // create a pair
        ReferenceArray refArrayPair = new ReferenceArray();
        if (curve is Line line)  //Maybe an arc is fine, too.
        {
            // Add the endpoints of the line for dimensioning
            refArrayPair.Append(curve.GetEndPointReference(0));
            refArrayPair.Append(curve.GetEndPointReference(1));
        }
        if (refArrayPair.Size == 2)
            refArrayPairs.Add(refArrayPair);
    }

    // Ensure at least two references for a valid dimension
    //if (references.Size < 2 || edgePoints.Count < 2)
    //{
    //    TaskDialog.Show("Error", "Not enough references to create a dimension.");
    //    return;
    //}

    // Determine dimension line direction based on edge points
    XYZ dimensionStart = edgePoints.First();
    XYZ dimensionEnd = edgePoints.Last();

    // Calculate the direction between the points
    XYZ dimensionDirection = dimensionEnd - dimensionStart;

    // Debugging: Show dimension line information
    TaskDialog.Show("Dimension Info", $"Start: {dimensionStart}\nEnd: {dimensionEnd}\nDirection: {dimensionDirection}");

    // Ensure the direction is valid for dimensioning (avoid small magnitude)
    if (dimensionDirection.IsZeroLength())
    {
        TaskDialog.Show("Error", "The start and end points are identical, invalid dimension direction.");
        return;
    }

    // Define a minimum threshold for valid dimension size
    double minDimensionSize = 0.01;  // Example: 0.01 units as the threshold (can be adjusted)

    // Check if the dimension size is too small
    if (dimensionDirection.GetLength() < minDimensionSize)
    {
        TaskDialog.Show("Warning", "The dimension size is too small to be valid.");
        return; // Skip creating dimension for too small sizes
    }

    // Check that the direction vector is valid and non-zero
    if (dimensionDirection.IsZeroLength() || dimensionDirection.DotProduct(XYZ.BasisZ) < 0.001)
    {
        TaskDialog.Show("Error", "The dimension direction is invalid. Please ensure the edge is valid for dimensioning.");
        return;
    }

    // Proceed with dimension creation
    Line dimensionLine = Line.CreateBound(dimensionStart, dimensionEnd);

    // Initialize a ReferenceArray to hold all the references
    ReferenceArray refArray = new ReferenceArray();

    // Add references to the ReferenceArray (for example, edge references)
    foreach (Edge edge in edges)
    {
        refArray.Append(edge.Reference);  // Add each edge's reference to the array
    }
    // Create the dimension using the line direction
    doc.Create.NewDimension(view, dimensionLine, refArray);
}

I update the code as per your instruction & run the code.

"Error: Sequence contains no elements" get this error.
please help me, how to solve this error.

0 Likes

Sleepingfish_Kuo
Enthusiast
Enthusiast
Accepted solution

Sleepingfish_Kuo_0-1737454379413.png

use this for next step.

foreach refArrayPairs, test and create a dim.

0 Likes

DesignGroup01
Enthusiast
Enthusiast

same issue again.

"Error: Sequence contains no elements" get this error.

0 Likes

Sleepingfish_Kuo
Enthusiast
Enthusiast

I didn't see your using "refArrayPair".

Since nothing is put in the list "edgepoint" but you're still using that, all codes are not working after that.

 

0 Likes