- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
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;
}
}
}
Solved! Go to Solution.