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: 

CreateViaOffset

10 REPLIES 10
Reply
Message 1 of 11
stephen_harrison
1512 Views, 10 Replies

CreateViaOffset

stephen_harrison
Advocate
Advocate

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.

CreateViaOffset

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.

10 REPLIES 10
Message 2 of 11

jeremytammik
Autodesk
Autodesk

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.

 



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

0 Likes

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.

 



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

Message 3 of 11

jeremytammik
Autodesk
Autodesk

Why tessellate your boundary curves? You can add the untessellated original curves to the curve loop, can't you? More elegant...

 



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

0 Likes

Why tessellate your boundary curves? You can add the untessellated original curves to the curve loop, can't you? More elegant...

 



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

Message 4 of 11

jeremytammik
Autodesk
Autodesk

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...

 



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

0 Likes

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...

 



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

Message 5 of 11

stephen_harrison
Advocate
Advocate

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.

Image 1.png

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?

Image 2.png

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.

Image 3.png

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

 

 

 

0 Likes

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.

Image 1.png

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?

Image 2.png

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.

Image 3.png

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

 

 

 

Message 6 of 11

jeremytammik
Autodesk
Autodesk

Can you share your sample model? Preferably including your complete add-in solution or macro or whatever it is? Thank you!

 



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

0 Likes

Can you share your sample model? Preferably including your complete add-in solution or macro or whatever it is? Thank you!

 



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

Message 7 of 11

stephen_harrison
Advocate
Advocate

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

0 Likes

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

Message 8 of 11

stephen_harrison
Advocate
Advocate

Sorry just noticed the model not attached.

 

Regards

0 Likes

Sorry just noticed the model not attached.

 

Regards

Message 9 of 11

Revitalizer
Advisor
Advisor

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




Rudolf Honke
Software Developer
Mensch und Maschine





0 Likes

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




Rudolf Honke
Software Developer
Mensch und Maschine





Message 10 of 11

stephen_harrison
Advocate
Advocate

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

0 Likes

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

Message 11 of 11

jeremytammik
Autodesk
Autodesk

> 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!

  



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

0 Likes

> 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!

  



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

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

Post to forums  

Autodesk Design & Make Report