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,
Solved! Go to Solution.
Solved by FAIR59. Go to Solution.
Solved by jeremytammik. Go to Solution.
Can you add an image to clarify exactly what you are after, and what dimension segment means? Thank you!
Hi Jeremy,
Here is an image to clarify what I tried to explain :
I have a dimension with 3 parts. My goal is to obtain this :
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
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
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.
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:
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
Ok thank you for your answer. I will wait for a future API changes.
Best regards
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());
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.
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; }
Perfect, thank you. You are faster than me
Last challenge is to obtain the two "pointing points" (in AutoCAD : xLinePoint1, xLinePoint2 and dimLinePoint).
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) ]
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.
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
Wouldn't it be safer to use the dim.View instead of doc.ActiveView?
i tested your code with a dimension spanning a sequence of three parallel walls.
running it, i see the following:
how would you suggest handling this?
should i make use of `e'`? how?
the code is checked in to The Building Coder samples in
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
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
@FAIR59 thank you!
i implemented it in
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:
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
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.