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: 

How to retrieve a Dimension's (segment) geometry ?

29 REPLIES 29
SOLVED
Reply
Message 1 of 30
maisoui
5070 Views, 29 Replies

How to retrieve a Dimension's (segment) geometry ?

Hello,

 

I'd like to retrieve the geometry of a Dimension : for each Dimension's segment, I'd like to have start, end points and text position and direction. I'm able to obtain the text position and the text string by looping on segments array :

 

foreach(DimensionSegment segment in dimension.Segments)
{
   //segment.TextPosition
   //segment.ValueString
}

But, I don't understand how to obtain the segment's points and direction. I tried get_geometry and looping on references array, but no success.

 

foreach(Reference reference in dimension.References)
{
    reference.GlobalPoint; // ---> always null
}

I don't find any samples.

Any suggestion is welcomed.

Best regards,

--
Jonathan
29 REPLIES 29
Message 2 of 30
jeremytammik
in reply to: maisoui

Can you add an image to clarify exactly what you are after, and what dimension segment means? Thank you!



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 30
maisoui
in reply to: jeremytammik

Hi Jeremy,

 

Here is an image to clarify what I tried to explain :

 

revit_dimension_geometry.png

I have a dimension with 3 parts. My goal is to obtain this :

  • Segment A : from point x to point y, text = "30" and text position = t1
  • Segment B : from point y to point z, text = "2.695" and text position = t2
  • Segment C : from point z to point w, text = "305" and text position = t3

I wrote "segments" beacause API, I found the method "Dimension.Segments" :

 

foreach(DimensionSegment segment in dimension.Segments)
{
   //segment.Origin : not the point I'm looking for
   //segment.LeaderEndPosition : not the point I'm looking for
   //segment.TextPosition : OK
   //segment.ValueString: OK
}

I hope my question is now clearer.

Best regards

--
Jonathan
Message 4 of 30
jeremytammik
in reply to: maisoui

Dear Jonathan,

 

Thank you for your explanation.

 

Yes, that helps a lot.

 

Have you looked at the Revit API help documentation on the Dimension class?

 

http://www.revitapidocs.com/2017/210f88be-e3c5-26a4-7dd8-3296f6725cce.htm

 

Doesn't the sample code presented there provide exactly what you are asking for via

 

dimension.Curve

 

?

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 30
maisoui
in reply to: jeremytammik

Thank you for your reply. Before sending my post, I searched this forum and I looked in the API samples and your blog, but without success.

In my example, the Dimension's is unbound : dimension.Curve.IsBound = false; So I have, an infinite line defined by a point and a direction. Maybe I need to project anything on it ? Maybe I habe to look references ?

I tried a lot of variants, but without success.

--
Jonathan
Message 6 of 30
jeremytammik
in reply to: maisoui

Dear Jonathan,

 

Thank you for testing and reporting this. How weird. How useless.

 

Funnily enough, a colleague of mine apparently ran into this very same issue yesterday and raised a development team issue for it:

 

We have an existing change request number REVIT-115341 [API Wish: Get end points of a linear dimension] for this issue, which will require exploration and possibly a modification to our software. Please make a note of this number for future reference. I have added a note of your request to this item in order to make the development team aware of its importance.

 

You are welcome to request an update on the status of this issue or to provide additional information on it at any time quoting this change request number.

 

This issue is important to me. What can I do to help?

 

This issue needs to be assessed by our engineering team, and prioritised against all other outstanding change requests. Any information that you can provide to influence this assessment will help. Please provide the following where possible:

 

  • Impact on your application and/or your development.
  • The number of users affected.
  • The potential revenue impact to you.
  • The potential revenue impact to Autodesk.
  • Realistic timescale over which a fix would help you.
  • In the case of a request for a new feature or a feature enhancement, please also provide detailed Use cases for the workflows that this change would address.

 

This information is extremely important. Our engineering team have limited resources, and so must focus their efforts on the highest impact items. We do understand that this will cause you delays and affect your development planning, and we appreciate your cooperation and patience.

 

You workaround ideas sounds eminently doable to me, e.g., use the ReferenceIntersector class to determine the appropriate element intersections and project them onto the unbounded dimension line. However, I do not see why this is required.

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 30
maisoui
in reply to: jeremytammik

Ok thank you for your answer. I will wait for a future API changes.

Best regards

--
Jonathan
Message 8 of 30
FAIR59
in reply to: maisoui

while I agree that the API should provide the points of a DimensionLine, you can calculate them.

 

StringBuilder sb = new StringBuilder();				
Dimension dim = doc.GetElement(sel.GetElementIds().FirstOrDefault()) as Dimension; Line dimLine = dim.Curve as Line; if (dimLine==null) return; dimLine.MakeBound(0,1); XYZ pt1 = dimLine.GetEndPoint(0); XYZ pt2 = dimLine.GetEndPoint(1); XYZ direction = pt2.Subtract(pt1).Normalize(); sb.AppendLine(string.Format("point1 {0}",pt1)); if (dim.Segments.Size == 0) { pt2 = pt1.Add(direction.Multiply((double) dim.Value)); sb.AppendLine(string.Format("point2 {0}",pt2)) ; } else { XYZ segmentPt0 = pt1; foreach( DimensionSegment seg in dim.Segments) { XYZ segmentPt1 = segmentPt0.Add(direction.Multiply((double) seg.Value)); sb.AppendLine(string.Format("pt {0}, value {1}",segmentPt1,(double) seg.Value)); segmentPt0 =segmentPt1; } } TaskDialog.Show("debug",sb.ToString());
Message 9 of 30
FAIR59
in reply to: FAIR59

sorry, I was too hasty. my solution won't work, pt1 is not the starting Point of the DimensionLine;

Message 10 of 30
maisoui
in reply to: FAIR59

I am trying your solution and I also noticed that there is a position issue.

Using the direction vector with the segment value is still a good idea.

 

A few comments:

 

Line line = dim.Curve as Line;

dimLine.MakeBound(0,1);
XYZ pt1 = dimLine.GetEndPoint(0); // ---> = line.Origin
XYZ pt2 = dimLine.GetEndPoint(1); // not needed
XYZ direction = pt2.Subtract(pt1).Normalize(); // ---> = line.Direction

Last thing:

segment.Origin

gives another point, but still not the correct position (start point).

 

Dimensions in Revit API, is really not clear to me.

--
Jonathan
Message 11 of 30
FAIR59
in reply to: maisoui

I think that in most cases this will get the startpoint of the dimensrion

 

				XYZ dimStartPoint=null;
				XYZ pt1 = dimLine.GetEndPoint(0);
				XYZ pt2 = null;
				foreach (Reference ref1 in dim.References)
				{
					XYZ refPoint = null;;
					Element el = doc.GetElement(	ref1.ElementId);
					GeometryObject obj = el.GetGeometryObjectFromReference(ref1);
					if (obj==null) 
					{
						// element is Grid or ReferencePlane or ??
						ReferencePlane refPl = el as ReferencePlane;
						if (refPl!= null) refPoint = refPl.GetPlane().Origin;
						Grid _grid = el as Grid;
						if (_grid!= null) refPoint = _grid.Curve.GetEndPoint(0);
					}
					else
					{
						// reference to Line or Plane 
						// or Point?
						Line l = obj as Line;
						if (l!=null) refPoint = l.GetEndPoint(0);
						PlanarFace f = obj as PlanarFace;
						if (f!=null) refPoint = f.Origin;
					}
					if (refPoint!=null) 
					{
						Plane WorkPlane = doc.ActiveView.SketchPlane.GetPlane();
						XYZ normal = WorkPlane.Normal.Normalize();
						// project the "globalpoint" of the reference onto the sketchplane
						XYZ refPtonPlane = refPoint.Subtract(normal.Multiply(normal.DotProduct(refPoint-WorkPlane.Origin)));
						XYZ LineNormal = normal.CrossProduct(direction).Normalize();
						// project the result onto the dimensionLine
						dimStartPoint = refPtonPlane.Subtract(LineNormal.Multiply(LineNormal.DotProduct( refPtonPlane-pt1)));
					}
					break;
				}
Message 12 of 30
maisoui
in reply to: FAIR59

Perfect, thank you. You are faster than me Smiley Wink

Last challenge is to obtain the two "pointing points" (in AutoCAD : xLinePoint1, xLinePoint2 and dimLinePoint).

 

revit_dimension_dimline.png

 

--
Jonathan
Message 13 of 30
FAIR59
in reply to: maisoui

I don't see a solution to your last question.

 

PS, I think the Origin of a segment is the MidPoint of that segment on the dimensionLine, so you can use the Origin of the first segment to find the startPoint of the dimension  [ Origin.Subtract (direction.Multiply(segment.Value /2) ]

Message 14 of 30
maisoui
in reply to: FAIR59

I'm looking to do something with property LeaderEndPosition.

 

I confirm that the segment's origin is the middle of the segment. So we don't need to iterate through references to fix point1.

Once again, thank you for your help.

--
Jonathan
Message 15 of 30
jeremytammik
in reply to: maisoui

Dear Jonathan,

Thank you for your confirmation and appreciation.

Can you please provide a minimal sample model containing a macro to demonstrate how it all works?

 

Alternatively, an external add-in would also be useful.

 

For documentation purposes and to enable others to make use of this also.

 

Thank you!

 

Cheers,

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 16 of 30
jeremytammik
in reply to: FAIR59

Wouldn't it be safer to use the dim.View instead of doc.ActiveView?



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 17 of 30
jeremytammik
in reply to: FAIR59

@FAIR59

 

i tested your code with a dimension spanning a sequence of three parallel walls.

 

running it, i see the following:

 

  • `el` is a wall.
  • `obj` is a valid geometry object, but neither a line nor a face, so the start point ends up remaining undefined.

 

how would you suggest handling this?

 

should i make use of `e'`? how?

 

the code is checked in to The Building Coder samples in 

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/C...

 

because the start point is undefined, the command currently prints out the following:

 

pt  (6.285955083, -4.935853072, 0.000000000),  value  3.28083989501312
pt  (9.566794978, -4.935853072, 0.000000000),  value  3.28083989501312
Start at , points (3.01,-4.94,0),(6.29,-4.94,0),(9.57,-4.94,0).

the start point before the comma is missing...

 

any idea?

 

thank you!

 

cheers,

 

jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 18 of 30
FAIR59
in reply to: jeremytammik

@jeremytammik

 

The obj is probably an Edge, so you need to adapt the code to get a Point from that Edge. In my testing I only considered Lines and PlanarFaces.

 

However, as I said in a previous post, you can find the startPoint of the dimensionline using the Dimension.Origin or DimensionSegment.Origin.

 

cheers

Message 19 of 30
jeremytammik
in reply to: FAIR59

@FAIR59 thank you!

 

i implemented it in 

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/C...

 

The entire code is:

 

    List<XYZ> GetDimensionPoints( Dimension dim )
    {
      Line dimLine = dim.Curve as Line;
      if( dimLine == null ) return null;
      List<XYZ> pts = new List<XYZ>();

      dimLine.MakeBound( 0, 1 );
      XYZ pt1 = dimLine.GetEndPoint( 0 );
      XYZ pt2 = dimLine.GetEndPoint( 1 );
      XYZ direction = pt2.Subtract( pt1 ).Normalize();
      pts.Add( pt1 );
      if( dim.Segments.Size == 0 )
      {
        pt2 = pt1.Add( direction.Multiply( (double) dim.Value ) );
        pts.Add( pt2 );
      }
      else
      {
        XYZ segmentPt0 = pt1;
        foreach( DimensionSegment seg in dim.Segments )
        {
          XYZ segmentPt1 = segmentPt0.Add( direction.Multiply( (double) seg.Value ) );
          Debug.Print( "pt  {0},  value  {1}", segmentPt1, (double) seg.Value );
          pts.Add( segmentPt1 );
          segmentPt0 = segmentPt1;
        }
      }
      return pts;
    }

    XYZ GetDimensionStartPointFirstAttempt(
      Dimension dim )
    {
      Document doc = dim.Document;

      Line dimLine = dim.Curve as Line;
      if( dimLine == null ) return null;
      dimLine.MakeBound( 0, 1 );

      XYZ dimStartPoint = null;
      XYZ pt1 = dimLine.GetEndPoint( 0 );

      // dim.Origin throws "Cannot access this method
      // if this dimension has more than one segment."
      //Debug.Assert( Util.IsEqual( pt1, dim.Origin ),
      //  "expected equal points" );

      foreach( Reference ref1 in dim.References )
      {
        XYZ refPoint = null;
        Element el = doc.GetElement( ref1.ElementId );
        GeometryObject obj = el.GetGeometryObjectFromReference(
          ref1 );

        if( obj == null )
        {
          // element is Grid or ReferencePlane or ??
          ReferencePlane refPl = el as ReferencePlane;
          if( refPl != null ) refPoint = refPl.GetPlane().Origin;
          Grid grid = el as Grid;
          if( grid != null ) refPoint = grid.Curve.GetEndPoint( 0 );
        }
        else
        {
          // reference to Line, Plane or Point?
          Line l = obj as Line;
          if( l != null ) refPoint = l.GetEndPoint( 0 );
          PlanarFace f = obj as PlanarFace;
          if( f != null ) refPoint = f.Origin;
        }

        if( refPoint != null )
        {
          //View v = doc.ActiveView;
          View v = dim.View;
          Plane WorkPlane = v.SketchPlane.GetPlane();
          XYZ normal = WorkPlane.Normal.Normalize();

          // Project the "globalpoint" of the reference onto the sketchplane

          XYZ refPtonPlane = refPoint.Subtract(
            normal.Multiply( normal.DotProduct(
              refPoint - WorkPlane.Origin ) ) );

          XYZ lineNormal = normal.CrossProduct(
            dimLine.Direction ).Normalize();

          // Project the result onto the dimensionLine

          dimStartPoint = refPtonPlane.Subtract(
            lineNormal.Multiply( lineNormal.DotProduct(
              refPtonPlane - pt1 ) ) );
        }
        break;
      }
      return dimStartPoint;
    }

    XYZ GetDimensionStartPoint(
      Dimension dim )
    {
      XYZ p = null;

      try
      {
        p = dim.Origin;
      }
      catch( Autodesk.Revit.Exceptions.ApplicationException ex )
      {
        Debug.Assert( ex.Message.Equals( "Cannot access this method if this dimension has more than one segment." ) );

        foreach( DimensionSegment seg in dim.Segments )
        {
          p = seg.Origin;
          break;
        }
      }
      return p;
    }

    public Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
    {
      UIApplication uiapp = commandData.Application;
      UIDocument uidoc = uiapp.ActiveUIDocument;
      Document doc = uidoc.Document;
      Selection sel = uidoc.Selection;

      ISelectionFilter f
        = new JtElementsOfClassSelectionFilter<Dimension>();

      Reference elemRef = sel.PickObject(
        ObjectType.Element, f, "Pick a dimension" );

      Dimension dim = doc.GetElement( elemRef ) as Dimension;

      XYZ p = GetDimensionStartPoint( dim );
      List<XYZ> pts = GetDimensionPoints( dim );

      int n = pts.Count;

      Debug.Print( "Dimension origin at {0} followed "
        + "by {1} further point{2}{3} {4}",
        Util.PointString( p ), n,
        Util.PluralSuffix( n ), Util.DotOrColon( n ),
        string.Join( ", ", pts.Select(
          q => Util.PointString( q ) ) ) );

      List<double> d = new List<double>( n );
      foreach( XYZ q in pts )
      {
        d.Add( q.X - p.X );
        p = q;
      }

      Debug.Print(
        "Horizontal distances in metres: "
        + string.Join( ", ", d.Select( x =>
          Util.RealString( Util.FootToMetre( x ) ) ) ) );

      return Result.Succeeded;
    }

However, when running it in this scenario:

 

Screen Shot 2017-06-15 at 13.39.10.png

In that scenario, the current code prints:

 

Dimension origin at (0.04,-4.94,0) followed by 3 further points: 
  (3.01,-4.94,0), (6.29,-4.94,0), (9.57,-4.94,0)

Horizontal distances in metres: 0.9, 1, 1

I guess that means that the '3 further points' really are the dimension points of interest.

 

The question is, what is the 'start point'?

 

Is it arbitrary?

 

In this case, it seems best to simply ignore it...

 

Thank you!

 

Cheers,

 

Jeremy

 

 

 

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 20 of 30
FAIR59
in reply to: jeremytammik

the arbitrary point is the point pt1 in GetDimensionPoints

 

I suggest

List<XYZ> GetDimensionPoints( Dimension dim )
    {
      Line dimLine = dim.Curve as Line;
      if( dimLine == null ) return null;
      List<XYZ> pts = new List<XYZ>();

      dimLine.MakeBound( 0, 1 );
      XYZ pt1 =  GetDimensionStartPoint( dim );
      XYZ pt2 = null;
      XYZ direction =  dimLine.Direction.Normalize();
      pts.Add( pt1 );

etc..

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

Post to forums  

Autodesk Customer Advisory Groups


Rail Community