Projection Transformation

richard.schaffranek
Enthusiast
Enthusiast

Projection Transformation

richard.schaffranek
Enthusiast
Enthusiast

Dear all,

 

I am relative new to revit api and was looking into transformations, trying to use them to project geometry (a curve) onto a plane. This should come out of the box, so I thought.

When apllying the following transformation

 

1,0,0,0

0,1,0,0

0,0,0,0

0,0,0,1 // for completness

 

to a curve I get a "transform is not conformal" error. 

Is there any way to do this in revit or do I need to reinvent the wheel and write the transformations for each curve type myself?

 

Best

 

Richard

0 Likes
Reply
Accepted solutions (2)
6,052 Views
18 Replies
Replies (18)

jeremytammik
Autodesk
Autodesk

Dear Richard,

 

I guess I said most of what I have to say on this in my answers to your comments on The Building Coder:

 

 

Using such a projection is obviously a good idea, in principle.

 

However, Revit transformations are built for BIM purposes.

 

You cannot apply a non-conformal transformation to a door or window and still expect it to comply with the building codes and standards.

 

So, how to project your 3D curve to 2D depends on what you want to achieve and do with it.

 

If you can live with an approximation, I would suggest you tessellate the curve.

 

That produces a series of line segments, and those are easy to project any way you like.

 

If that is not an option and you need precise curve results, I am indeed afraid you might have to write the transformations for each curve type yourself.

 

Alternatively, you might be able to hook up with some external geometric library that can support you in that transformation.

 

I have researched that topic briefly multiple times, and all the 3D libraries that I looked at were rather large and complex for my immediate needs.

 

However, I think any good reliable powerful library that you can connect will do the job.

 

So, I have no specific preference in that area. There are a huge number of them out there, some are immensely powerful, some are good and relatively unknown. Several CAD vendors have open-sourced significant libraries.

 

Please do let us know how you end up solving this.

 

Thank you!

 

Best regards,

 

Jeremy

 



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

0 Likes

richard.schaffranek
Enthusiast
Enthusiast

Well I went the long way and wrote projection methodes for all curve types (except CylindricalHelix ), as for lines it is straight forward, but I am not so sure with hermitSplines / NurbsSplines, maybe someone could comment on that, maybe there are problems I have missed...

I have tested it for some occations and two things that happened where:

 

a) projected curves where longer than edges of faces from which they where generated...

b) projected hermitSplines didn't rebuild correctly, which might be becaus you can only define start / end tangents and not the complete list when creating a curve.

c) Ellipse.createcurve() is a little too clever since it will create an arc if the two radius are equal... -> resulted in a endless loop... 

 

        private static Curve Project(Autodesk.Revit.DB.NurbSpline nurbsSpline, Autodesk.Revit.DB.Plane plane)
        {

            XYZ[] controlPoints = GeometryHelper.ProjectPoint(nurbsSpline.CtrlPoints, plane);
            double[] knots = new double[nurbsSpline.Knots.Size];
            for(int i = 0; i < knots.Length;i++)
            {
                knots[i] = nurbsSpline.Knots.get_Item(i);
            }
            double[] weights = new double[nurbsSpline.Weights.Size];
            for (int i = 0; i < weights.Length; i++)
            {
                weights[i] = nurbsSpline.Weights.get_Item(i);
            }
            return NurbSpline.CreateCurve(nurbsSpline.Degree, knots,  controlPoints, weights);
         }

 

        private static Curve Project(Autodesk.Revit.DB.HermiteSpline hermiteSpline, Autodesk.Revit.DB.Plane plane)
        { 
            XYZ[] tangents= GeometryHelper.ProjectVector(hermiteSpline.Tangents,plane);
            XYZ[] controlPoints = GeometryHelper.ProjectPoint(hermiteSpline.ControlPoints, plane);
            HermiteSplineTangents hermiteSplineTangents = new HermiteSplineTangents() { StartTangent = tangents[0].Normalize(),EndTangent = tangents[tangents.Length - 1].Normalize() };
            return HermiteSpline.Create(controlPoints, hermiteSpline.IsPeriodic, hermiteSplineTangents);
        }

 

 

 

 

 

jeremytammik
Autodesk
Autodesk

Well done! Sounds like pretty good progress. I cannot say anything off-hand about the issues you observed, though, so I passed on your questions to the development team for you. I hope to hear back from them soon. 

  



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

0 Likes

jeremytammik
Autodesk
Autodesk
Accepted solution

Feedback so far from the development team:

 

If Revit's public API does not include a function to project a curve to a plane (or more generally, to a surface), and it looks like it doesn't, you can request that such a function be added. This wouldn't be done immediately. Note that there are various complications - the curve may project to a self-intersecting curve, or one with singularities such as cusps, or to a degenerate curve (a point), and such curves may cause downstream problems. For projecting a curve onto a surface, part of the curve may miss the surface when projected, and one might want to distinguish between projecting orthogonal to the surface and projecting along a given direction.

  

You could file a wish list item for this in the Revit Idea Station and ensure it gets many votes. However, it would take time to plan and implement, of course.

 

I'll let you know if they add anything else, e.g., take a look at and comment on your sample code.

 



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

Revitalizer
Advisor
Advisor
Accepted solution

Hi,

 

there is a workaround for the projection of curves to a plane.

 

From your curves, create ModelCurves.

 

Depending on context (project or family), you can create DetailCurves or SymbolicCurves in a given view.

 

Document.ConvertModelToDetailCurves or

Document.ConvertModelToSymbolicCurves

 

In the RevitAPI.chm, it says in the text:

 

"The lines are projected on the view plane. If the lines are not parallel to the view plane, lines are foreshortened and arcs are converted to ellipses. Splines are modified."

Get the DetailCurve.GeometryCurve from the resulting elements and your are done.

 

Since you cannot create (2D) views for arbitrary planes but for vertical or horizontal ones only, this workaround is limited.

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





richard.schaffranek
Enthusiast
Enthusiast

Thanks for the effort, dont't get me wrong when saying this, I doubt that something is going to happen...  but I had a little conversation incuding a wish for Inventor a while ago. It wasn't accepting surface generated from another program  (Rhino3d) for unrolling them even so I got the feedback that the surface was perfectly developable.

 

So that didn't change...

 

 

 

0 Likes

jeremytammik
Autodesk
Autodesk

Wow, Rudi, absolutely brilliant!

 

If you really want to work around the restriction to horizontal and vertical planes, you can add another twist (plus reverse twist) to the process as follows:

 

  • Apply a suitable transformation to the curve before converting it to a model line
  • Project the model line onto a horizontal or vertical plane
  • Apply the reverse transformation to the horizontal or vertical planar result to get it positioned where you want it

 

Haha!

 



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

0 Likes

Revitalizer
Advisor
Advisor

Hi Jeremy,

 

simple task, surprising workaround - that's Revit API at its best.

 

That would be a nice entry for your blog.

 

 

Rudi




Rudolf Honke
Software Developer
Mensch und Maschine





0 Likes

richard.schaffranek
Enthusiast
Enthusiast

Definitly a thing to try out. I had some issues with preciosion and further boolean operations with my projection algorithm, I am wondering if this is a more precise way to do so?

 

Have to test.

0 Likes

Revitalizer
Advisor
Advisor

Hi,

 

as for precision, I think that there may be inaccuracies, due to the fact that we create elements (which react to other elements, views etc.).

 

Revit's AI may adjust elements according to its will.

 

Also, further limitations are apparent.

There is a ShortCurveTolerance constant, "the enforced minimum length for any curve created by Revit", as the documentation says.

 

So the conversion methods will fail if a projected result would contain such a curve.

Imagine a line in the plane's normal direction - it would result in a point on the plane.

 

But after all, I didn't try this myself.

 

You could project some control points to the plane (start point, end point) and check the projected curves' related points.

So you could get a tolerance value.

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





0 Likes

richard.schaffranek
Enthusiast
Enthusiast

Will give it a try, but might stick to my implmenetation, at least I know were the inprecision is...

0 Likes

jeremytammik
Autodesk
Autodesk

It would be absolutely great if you could share your current implementation with all its imprecision aspects documented plus the results of testing Rudi's suggestion. That would be awesome material for a nicely rounded-off discussion by The Building Coder. Thank you!

 



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

0 Likes

jeremytammik
Autodesk
Autodesk

Here is a comment from the development team on your projection implementation:

  

In the second function, which projects a Hermite spline curve onto a plane, the user should not normalise the tangent vectors. The lengths of the start and end tangent vectors (i.e., first derivatives) affect the shape of the spline curve.The first function, which projects Nurbs spline curves onto a plane, looks correct. The user didn't seem to indicate any problems with that function, but if I misunderstood, let me know. The fact that the Ellipse constructor will create an Arc if the ellipse happens to be circular is intentional, because Revit expects curves and surfaces to be represented in the simplest possible form, with an arc being considered simpler than an ellipse. I don't know what the user's first comment means ("projected curves where longer than edges of faces from which they where generated").
 
I hope this helps.
 


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

0 Likes

jeremytammik
Autodesk
Autodesk

Some more feedback from the development team on the ideas discussed above:

 

Regarding Document.ConvertModelToDetailCurves and Document.ConvertModelToSymbolicCurves: the user should be aware that if they produce curves that Revit considers invalid, such as self-intersecting curves or zero-length curves, they may cause problems if they're used in creating Revit geometry (or in other contexts for that matter). However, a quick look at the internal function those API functions call indicates that it does check if the projection curve (i.e., the result) would have singularities or self-intersections and does not return a result in such cases. So, I would guess that the same is true for the two API functions.

 

Note, too, that there are cases when users might want to allow the projection curve to be self-intersecting. For example, if a full circle lying in a vertical plane is projected to a horizontal plane, some users might want that operation to succeed and to return a line segment representing the "shadow" of the circle (as if the sun were directly overhead). I don't believe the Revit API provides such a function.



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

0 Likes

jeremytammik
Autodesk
Autodesk

Dear Richard,

 

How did you end up solving this, please, so we can wrap it up?

 

Thank you!

 

Cheers,

 

Jeremy

 



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

ankofl
Advocate
Advocate

Dear Jeremy, I have written several methods for projecting points on a plane and visualizing the calculated geometry, for a better understanding of what is happening

A method of projecting a point onto a plane defined by two points p1 and p2

public static XYZ PointAtPlane(XYZ point, XYZ start, XYZ end)
        {
            // Find the vector "v" between the start and end points:
            XYZ v = end - start;
            // Find the unit vector "n" of the normal to the plane passing through the given start and end points:
            XYZ n = v.Normalize();
            // Find the vector "u" from the center of the plane start to a given point point:
            XYZ u = point - start;
            // Find the distance "d" from the center of the plane to the projection of the point point on the plane:
            double d = u.DotProduct(n);
            // Find the projection p of a given point point on a plane:
            XYZ result = point - d * n;
            return result;
        }



This method creates a square in the plane of the facade "In front"

 

//This method creates a square in the plane of the facade "In front"
public static List<XYZ> CreateRect(double hight, double wight)
        {
            return new List<XYZ>
            {
                new XYZ(-wight / 2, 0, hight / 2), // leftUp
                new XYZ(wight / 2, 0, hight / 2), // rightUp
                new XYZ(wight / 2, 0, -hight / 2), // rightDown
                new XYZ(-wight / 2, 0, -hight / 2) // leftDown
            };
        }

 

Next, using the Create Model Line method (described below), we draw lines in the model that visualize all the vectors of the points used.

 

public static List<XYZ> CreateVisual(XYZ p1, XYZ p2, List<XYZ> listPoint)
        {
            Element red = Main.Doc.GetElement(new ElementId(490985));
            Element orange = Main.Doc.GetElement(new ElementId(421355));
            Element green = Main.Doc.GetElement(new ElementId(78));

            CreateModelLine(Main.Doc, p1, p2, red);
            List<XYZ> lPtAtPl = new List<XYZ>();
            foreach (XYZ point in listPoint)
            {
                CreateModelLine(Main.Doc, XYZ.Zero, point, orange);
                XYZ p = PointAtPlane(point, p1, p2);
                CreateModelLine(Main.Doc, XYZ.Zero, p, green);
                lPtAtPl.Add(p);
            }
        }

 

The "red" line style is used to display the line that defines the normal of the plane on which the point vectors of the previously created rectangle should be projected. The orange line style displays the point vectors of the original rectangle. The blue lines represent just the projection vectors of the points of the original rectangle on the plane specified by points p1 and p2
Demonstration of the projection of the rectangle createRect(10, 10) and the plane defined by the points new XYZ(0,0,0) and new XYZ(1, 2, 2)

ankofl_0-1682669915486.png  ankofl_1-1682670273077.png

The ModelCurve creation method is described below

 

public static ModelCurve CreateModelLine(Document Doc, XYZ start, XYZ end, Element lineStyle)
        {
            ModelCurve modelLine = null;
            try
            {
                Line geomLine = Line.CreateBound(start, end);

                XYZ lineDirection = end - start;

                XYZ normal = lineDirection.CrossProduct(XYZ.BasisZ).Normalize();

                if (normal.IsZeroLength())
                {
                    normal = lineDirection.CrossProduct(XYZ.BasisY).Normalize();

                    if (normal.IsZeroLength())
                    {
                        normal = lineDirection.CrossProduct(XYZ.BasisX).Normalize();
                    }
                }

                Plane plane = Plane.CreateByNormalAndOrigin(normal, start);

                Plane geomPlane = Plane.CreateByNormalAndOrigin(plane.Normal, start);

                SketchPlane sketch = SketchPlane.Create(Doc, geomPlane);

                modelLine = Doc.Create.NewModelCurve(geomLine, sketch);
                modelLine.LineStyle = lineStyle;
            }
            catch { }
            return modelLine;
        }

 

 

0 Likes

jeremy_tammik
Autodesk
Autodesk

Thank you for sharing. I am travelling right now. I'll take a closer look asap. Meanwhile, can you compare your methods with similar functions in The Building Coder samples Creator and Util modules?

  

  

        /// <summary>
        ///     Project given 3D XYZ point onto plane.
        /// </summary>
        public static XYZ ProjectOnto(
            this Plane plane,
            XYZ p)

   

Thank you!

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

ankofl
Advocate
Advocate

Thanks for the link, I haven't seen it before. Regarding my solution, I think I have found a more optimal solution that does the work of my PointAtPlane method. It was found in CmdDetailCurves.cs in the BuildingCoder folder by your link

private XYZ ProjectPointOntoPlane(
            XYZ point,
            XYZ planeNormal)
        {
            var a = planeNormal.X;
            var b = planeNormal.Y;
            var c = planeNormal.Z;

            var dx = (b * b + c * c) * point.X - a * b * point.Y - a * c * point.Z;
            var dy = -(b * a) * point.X + (a * a + c * c) * point.Y - b * c * point.Z;
            var dz = -(c * a) * point.X - c * b * point.Y + (a * a + b * b) * point.Z;
            return new XYZ(dx, dy, dz);
        }
0 Likes