I hope some one can help as I have been going round in circles with this for some time. I am trying to create a CurveLoop CreateViaOffset
I have looked at most if not all of the posts on this subject and every time i think i have a working solution either through utilising those solutions posted or my own and i constantly obtain an error of one kind or another.
My Code for the CurveLoop is
public void CropAroundRoom(Autodesk.Revit.DB.Architecture.Room room, Autodesk.Revit.DB.View view) { if (view != null) { IList<IList<Autodesk.Revit.DB.BoundarySegment>> segments = room.GetBoundarySegments(new SpatialElementBoundaryOptions()); if (null != segments) { foreach (IList<Autodesk.Revit.DB.BoundarySegment> segmentList in segments) { CurveLoop loop = new CurveLoop(); foreach (Autodesk.Revit.DB.BoundarySegment boundarySegment in segmentList) { List<XYZ> points = boundarySegment.GetCurve().Tessellate().ToList(); for(int ip=0; ip< points.Count-1; ip++) { Line l = Line.CreateBound(points[ip],points[ip+1]); loop.Append(l); } } ViewCropRegionShapeManager vcrShapeMgr = view.GetCropRegionShapeManager(); bool cropValid = vcrShapeMgr.IsCropRegionShapeValid(loop); if (cropValid) { vcrShapeMgr.SetCropShape(loop); break; } } } } }
I have also utilised the BuidingCoder GetCurveNormal and from examining the returned XYZ they were (1,0,0), (0,1,0),(0,1,0),(-1,0,0) and (0,-1,0).
The help Syntax for CreateViaOffset
public static CurveLoop CreateViaOffset(CurveLoop original,double offsetDist,XYZ normal)
Therefore I passed in my loop created above, a double value of 2.0 and one of the normal values obtained from GetCurveNormal and unfortunately an error.
I then thought that I had misunderstood an tried passing a normal for each curve of the curve loop. Error.
Ultimately my objective is to create a new CurveLoop some fixed distance beyond the room.
If at all possible I would also like to be able to create alternate offsets for each face even if that means utilising an alternate to CreateViaOffset.
Any help would be much appreciated.
I hope some one can help as I have been going round in circles with this for some time. I am trying to create a CurveLoop CreateViaOffset
I have looked at most if not all of the posts on this subject and every time i think i have a working solution either through utilising those solutions posted or my own and i constantly obtain an error of one kind or another.
My Code for the CurveLoop is
public void CropAroundRoom(Autodesk.Revit.DB.Architecture.Room room, Autodesk.Revit.DB.View view) { if (view != null) { IList<IList<Autodesk.Revit.DB.BoundarySegment>> segments = room.GetBoundarySegments(new SpatialElementBoundaryOptions()); if (null != segments) { foreach (IList<Autodesk.Revit.DB.BoundarySegment> segmentList in segments) { CurveLoop loop = new CurveLoop(); foreach (Autodesk.Revit.DB.BoundarySegment boundarySegment in segmentList) { List<XYZ> points = boundarySegment.GetCurve().Tessellate().ToList(); for(int ip=0; ip< points.Count-1; ip++) { Line l = Line.CreateBound(points[ip],points[ip+1]); loop.Append(l); } } ViewCropRegionShapeManager vcrShapeMgr = view.GetCropRegionShapeManager(); bool cropValid = vcrShapeMgr.IsCropRegionShapeValid(loop); if (cropValid) { vcrShapeMgr.SetCropShape(loop); break; } } } } }
I have also utilised the BuidingCoder GetCurveNormal and from examining the returned XYZ they were (1,0,0), (0,1,0),(0,1,0),(-1,0,0) and (0,-1,0).
The help Syntax for CreateViaOffset
public static CurveLoop CreateViaOffset(CurveLoop original,double offsetDist,XYZ normal)
Therefore I passed in my loop created above, a double value of 2.0 and one of the normal values obtained from GetCurveNormal and unfortunately an error.
I then thought that I had misunderstood an tried passing a normal for each curve of the curve loop. Error.
Ultimately my objective is to create a new CurveLoop some fixed distance beyond the room.
If at all possible I would also like to be able to create alternate offsets for each face even if that means utilising an alternate to CreateViaOffset.
Any help would be much appreciated.
Look at the method OffsetPoints in The Building Coder samples:
/// <summary> /// Offset a list of points by a distance in a /// given direction in or out of the curve loop. /// </summary> public static IEnumerable<XYZ> OffsetPoints( List<XYZ> pts, double offset, XYZ normal ) { CurveLoop curveLoop = CreateCurveLoop( pts ); CurveLoop curveLoop2 = CurveLoop.CreateViaOffset( curveLoop, offset, normal ); return curveLoop2.Select<Curve, XYZ>( c => c.GetEndPoint( 0 ) ); }
It was implemented to support offsetting of point with no edges or curves attached and seems to solve the problem discussed here:
https://forums.autodesk.com/t5/revit-api-forum/offset-xyz-points/m-p/8665334
Your curve loop needs to be closed and contiguous, I assume, all edges pointing in the same direction.
Then, if it is in the XY plane, providing (0,0,1) as the normal vector will offset inwards, and (0,0,-1) outwards, or vice versa, depending on whether your curve loop is oriented clockwise or anticlockwise.
Look at the method OffsetPoints in The Building Coder samples:
/// <summary> /// Offset a list of points by a distance in a /// given direction in or out of the curve loop. /// </summary> public static IEnumerable<XYZ> OffsetPoints( List<XYZ> pts, double offset, XYZ normal ) { CurveLoop curveLoop = CreateCurveLoop( pts ); CurveLoop curveLoop2 = CurveLoop.CreateViaOffset( curveLoop, offset, normal ); return curveLoop2.Select<Curve, XYZ>( c => c.GetEndPoint( 0 ) ); }
It was implemented to support offsetting of point with no edges or curves attached and seems to solve the problem discussed here:
https://forums.autodesk.com/t5/revit-api-forum/offset-xyz-points/m-p/8665334
Your curve loop needs to be closed and contiguous, I assume, all edges pointing in the same direction.
Then, if it is in the XY plane, providing (0,0,1) as the normal vector will offset inwards, and (0,0,-1) outwards, or vice versa, depending on whether your curve loop is oriented clockwise or anticlockwise.
Why tessellate your boundary curves? You can add the untessellated original curves to the curve loop, can't you? More elegant...
Why tessellate your boundary curves? You can add the untessellated original curves to the curve loop, can't you? More elegant...
Since you loop over each of the `segmentList` objects and call `SetCropShape` repeatedly for each one of them, the last one in your loop will win, and all other settings will be overwritten by that.
Have you checked whether the first boundary segment loop is the outer one, always, reliably?
If so, you will probably be better off just using that, and skipping all the subsequent (presumably inner) loops.
Oh dear, a room can be split into several components as well, can't it? In that case, there may be several disjunct outer loops.
Lots of special cases to handle... if the BIM creator is really weird, that is...
Since you loop over each of the `segmentList` objects and call `SetCropShape` repeatedly for each one of them, the last one in your loop will win, and all other settings will be overwritten by that.
Have you checked whether the first boundary segment loop is the outer one, always, reliably?
If so, you will probably be better off just using that, and skipping all the subsequent (presumably inner) loops.
Oh dear, a room can be split into several components as well, can't it? In that case, there may be several disjunct outer loops.
Lots of special cases to handle... if the BIM creator is really weird, that is...
Thank you Jeremy for you responses.
When working on my code i like to create small working packages and a test revit project to test them on.
The image below is my test revit project with 5 rooms.
The reason i tessellate the boundary curve is that it is the only way i have been able to get a boundary curve that follows the boundary my test rooms of which some have curved walls see image below?
The fact that my original code above provides a cropped boundary would indicate that the original curve loop is closed and continuous.
I have also assumed it is the tessellated boundary points that i need to utilise to get the offset Cropped Boundary Curve?
I had previously tested the code you suggested but have tested again and still with no look the full code is below.
public void CropAroundRoom(Autodesk.Revit.DB.Architecture.Room room, Autodesk.Revit.DB.View view) { List<XYZ> points = new List<XYZ>(); if (view != null) { IList<IList<Autodesk.Revit.DB.BoundarySegment>> segments = room.GetBoundarySegments(new SpatialElementBoundaryOptions()); if (null != segments) { foreach (IList<Autodesk.Revit.DB.BoundarySegment> segmentList in segments) { CurveLoop loop = new CurveLoop(); foreach (Autodesk.Revit.DB.BoundarySegment boundarySegment in segmentList) { points.AddRange(boundarySegment.GetCurve().Tessellate()); } CurveLoop loop2 = new CurveLoop(); double offset = 2.0; XYZ normal = new XYZ(0,0,-1); List<XYZ> pts = OffsetPoints(points,offset,normal).ToList(); for(int ip=0; ip< points.Count-1; ip++) { Line l = Line.CreateBound(pts[ip],pts[ip+1]); loop2.Append(l); } ViewCropRegionShapeManager vcrShapeMgr = view.GetCropRegionShapeManager(); bool cropValid = vcrShapeMgr.IsCropRegionShapeValid(loop); if (cropValid) { vcrShapeMgr.SetCropShape(loop); break; } } } } } public static CurveLoop CreateCurveLoop(List<XYZ> pts ) { int n = pts.Count; CurveLoop curveLoop = new CurveLoop(); for( int i = 1; i < n; ++i ) { curveLoop.Append( Line.CreateBound(pts[i - 1], pts[i] ) ); } curveLoop.Append( Line.CreateBound(pts[n], pts[0] ) ); return curveLoop; } public static IEnumerable<XYZ> OffsetPoints( List<XYZ> pts,double offset,XYZ normal ) { CurveLoop curveLoop = CreateCurveLoop( pts ); CurveLoop curveLoop2 = CurveLoop.CreateViaOffset( curveLoop, offset, normal ); return curveLoop2.Select<Curve, XYZ>( c => c.GetEndPoint( 0 ) ); }
On the 2nd pass through the CreateCurveLoop for loop it crashed with the following error "Curve length is too small for Revit's tolerance (as identified by Application.ShortCurveTolerance)" which may be understandable if the room was very small but it is 4.5m x 3.5m
I have also indicated below the List<XYZ> pts data passed into OffsetPoints.
I am at a loss as to what i am doing wrong?
As can be seen from my test project the room are simple and the boundary segment loop will be the outer.
For more complex situations were this is not the case I assume the following is still the best solution.
https://thebuildingcoder.typepad.com/blog/2008/12/2d-polygon-areas-and-outer-loop.html
This will be my next part of the code to resolve once I can get the CreateViaOffset to work.
Regards
Stephen
Thank you Jeremy for you responses.
When working on my code i like to create small working packages and a test revit project to test them on.
The image below is my test revit project with 5 rooms.
The reason i tessellate the boundary curve is that it is the only way i have been able to get a boundary curve that follows the boundary my test rooms of which some have curved walls see image below?
The fact that my original code above provides a cropped boundary would indicate that the original curve loop is closed and continuous.
I have also assumed it is the tessellated boundary points that i need to utilise to get the offset Cropped Boundary Curve?
I had previously tested the code you suggested but have tested again and still with no look the full code is below.
public void CropAroundRoom(Autodesk.Revit.DB.Architecture.Room room, Autodesk.Revit.DB.View view) { List<XYZ> points = new List<XYZ>(); if (view != null) { IList<IList<Autodesk.Revit.DB.BoundarySegment>> segments = room.GetBoundarySegments(new SpatialElementBoundaryOptions()); if (null != segments) { foreach (IList<Autodesk.Revit.DB.BoundarySegment> segmentList in segments) { CurveLoop loop = new CurveLoop(); foreach (Autodesk.Revit.DB.BoundarySegment boundarySegment in segmentList) { points.AddRange(boundarySegment.GetCurve().Tessellate()); } CurveLoop loop2 = new CurveLoop(); double offset = 2.0; XYZ normal = new XYZ(0,0,-1); List<XYZ> pts = OffsetPoints(points,offset,normal).ToList(); for(int ip=0; ip< points.Count-1; ip++) { Line l = Line.CreateBound(pts[ip],pts[ip+1]); loop2.Append(l); } ViewCropRegionShapeManager vcrShapeMgr = view.GetCropRegionShapeManager(); bool cropValid = vcrShapeMgr.IsCropRegionShapeValid(loop); if (cropValid) { vcrShapeMgr.SetCropShape(loop); break; } } } } } public static CurveLoop CreateCurveLoop(List<XYZ> pts ) { int n = pts.Count; CurveLoop curveLoop = new CurveLoop(); for( int i = 1; i < n; ++i ) { curveLoop.Append( Line.CreateBound(pts[i - 1], pts[i] ) ); } curveLoop.Append( Line.CreateBound(pts[n], pts[0] ) ); return curveLoop; } public static IEnumerable<XYZ> OffsetPoints( List<XYZ> pts,double offset,XYZ normal ) { CurveLoop curveLoop = CreateCurveLoop( pts ); CurveLoop curveLoop2 = CurveLoop.CreateViaOffset( curveLoop, offset, normal ); return curveLoop2.Select<Curve, XYZ>( c => c.GetEndPoint( 0 ) ); }
On the 2nd pass through the CreateCurveLoop for loop it crashed with the following error "Curve length is too small for Revit's tolerance (as identified by Application.ShortCurveTolerance)" which may be understandable if the room was very small but it is 4.5m x 3.5m
I have also indicated below the List<XYZ> pts data passed into OffsetPoints.
I am at a loss as to what i am doing wrong?
As can be seen from my test project the room are simple and the boundary segment loop will be the outer.
For more complex situations were this is not the case I assume the following is still the best solution.
https://thebuildingcoder.typepad.com/blog/2008/12/2d-polygon-areas-and-outer-loop.html
This will be my next part of the code to resolve once I can get the CreateViaOffset to work.
Regards
Stephen
Can you share your sample model? Preferably including your complete add-in solution or macro or whatever it is? Thank you!
Can you share your sample model? Preferably including your complete add-in solution or macro or whatever it is? Thank you!
Thank you Jeremy for your time and your assistance is much appreciated.
Attached is the test model I have been utilising, you will note it has changed slightly with a room within a room in order that I could carry out some additional tests on how this may affect the BoundarySegment with specific reference to your comment regarding Segment Loop being the outer one.
I hope everything is ok to follow as it is all rough and ready test code.
Please not it is for Revit 2018.3 but that is purely for my own convenience.
Regards
Thank you Jeremy for your time and your assistance is much appreciated.
Attached is the test model I have been utilising, you will note it has changed slightly with a room within a room in order that I could carry out some additional tests on how this may affect the BoundarySegment with specific reference to your comment regarding Segment Loop being the outer one.
I hope everything is ok to follow as it is all rough and ready test code.
Please not it is for Revit 2018.3 but that is purely for my own convenience.
Regards
Sorry just noticed the model not attached.
Regards
Sorry just noticed the model not attached.
Regards
Hi,
I think there may be a problem because of too much self-intersections, inside the Revit API method itself.
Imagine all the small line segments made from the curve, drawn in a given distance.
In fact, they form another arc, but somewhat twisted.
Why not create an offset loop of the boundary containing the original curve and just tesselating the result - if necessary at all?
Revitalizer
Hi,
I think there may be a problem because of too much self-intersections, inside the Revit API method itself.
Imagine all the small line segments made from the curve, drawn in a given distance.
In fact, they form another arc, but somewhat twisted.
Why not create an offset loop of the boundary containing the original curve and just tesselating the result - if necessary at all?
Revitalizer
Thank you for your suggestion.
Tessellating the result of the offset Loop does appear to resolve the problem.
Thankyou.
You also question why tessellate.
It is my objective to create a loop that follows the shape of the room and were there are curves (as indicated in the images above) the only way I managed to achieve this was by utilising Tessellation.
Is there a better alternative to achieve the same goal?
Regards
Thank you for your suggestion.
Tessellating the result of the offset Loop does appear to resolve the problem.
Thankyou.
You also question why tessellate.
It is my objective to create a loop that follows the shape of the room and were there are curves (as indicated in the images above) the only way I managed to achieve this was by utilising Tessellation.
Is there a better alternative to achieve the same goal?
Regards
> Is there a better alternative to achieve the same goal?
Yes, I believe so. The curve you tessellate is a curve. The curve loop you are assembling from the tessellation line segments is a collection of curves.
I assume you can skip the tessellation completely and just add the original curve to the curve loop as is.
KISS!
> Is there a better alternative to achieve the same goal?
Yes, I believe so. The curve you tessellate is a curve. The curve loop you are assembling from the tessellation line segments is a collection of curves.
I assume you can skip the tessellation completely and just add the original curve to the curve loop as is.
KISS!
Can't find what you're looking for? Ask the community or share your knowledge.